Skip to content

Commit dd70e03

Browse files
committed
Intern column names so we always get the same string
When we get column names back from the database, it's very common to always return the same strings. This patch uses Ruby's "interned string" API so that we're always getting the same string objects back from the database. Fixes: #155
1 parent 44ab2aa commit dd70e03

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

ext/sqlite3/extconf.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ def configure_extension
109109

110110
abort_could_not_find(libname) unless find_library(libname, "sqlite3_libversion_number", "sqlite3.h")
111111

112+
# Truffle Ruby doesn't support this yet:
113+
# https://github.com/oracle/truffleruby/issues/3408
114+
have_func("rb_enc_interned_str_cstr")
115+
112116
# Functions defined in 1.9 but not 1.8
113117
have_func("rb_proc_arity")
114118

ext/sqlite3/statement.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,22 @@ column_count(VALUE self)
364364
return INT2NUM(sqlite3_column_count(ctx->st));
365365
}
366366

367+
#if HAVE_RB_ENC_INTERNED_STR_CSTR
368+
static VALUE
369+
interned_utf8_cstr(const char * str)
370+
{
371+
return rb_enc_interned_str_cstr(str, rb_utf8_encoding());
372+
}
373+
#else
374+
static VALUE
375+
interned_utf8_cstr(const char * str)
376+
{
377+
VALUE rb_str = rb_utf8_str_new_cstr(str);
378+
rb_obj_freeze(rb_str);
379+
return rb_funcall(rb_str, rb_intern("-@"), 0);
380+
}
381+
#endif
382+
367383
/* call-seq: stmt.column_name(index)
368384
*
369385
* Get the column name at +index+. 0 based.
@@ -382,8 +398,7 @@ column_name(VALUE self, VALUE index)
382398
VALUE ret = Qnil;
383399

384400
if (name) {
385-
ret = SQLITE3_UTF8_STR_NEW2(name);
386-
rb_obj_freeze(ret);
401+
ret = interned_utf8_cstr(name);
387402
}
388403
return ret;
389404
}

test/test_statement.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ def test_raises_type_error
4848
end
4949
end
5050

51+
def test_column_names_are_deduped
52+
@db.execute "CREATE TABLE 'things' ('float' float, 'int' int, 'text' blob, 'string' string, 'nil' string)"
53+
stmt = @db.prepare "SELECT float, int, text, string, nil FROM things"
54+
assert_equal ["float", "int", "text", "string", "nil"], stmt.columns
55+
columns = stmt.columns
56+
stmt.close
57+
58+
stmt = @db.prepare "SELECT float, int, text, string, nil FROM things"
59+
# Make sure this new statement returns the same interned strings
60+
stmt.columns.each_with_index do |str, i|
61+
assert_same columns[i], str
62+
end
63+
ensure
64+
stmt&.close
65+
end
66+
5167
def test_insert_duplicate_records
5268
@db.execute 'CREATE TABLE "things" ("name" varchar(20) CONSTRAINT "index_things_on_name" UNIQUE)'
5369
stmt = @db.prepare("INSERT INTO things(name) VALUES(?)")

0 commit comments

Comments
 (0)