Skip to content

Commit

Permalink
Merge pull request #486 from sparklemotion/intern-column-names
Browse files Browse the repository at this point in the history
Intern column names so we always get the same string
  • Loading branch information
flavorjones committed Jan 27, 2024
2 parents 85bd68d + 62c24b6 commit ea0cb80
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 2 deletions.
4 changes: 4 additions & 0 deletions ext/sqlite3/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ def configure_extension

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

# Truffle Ruby doesn't support this yet:
# https://github.com/oracle/truffleruby/issues/3408
have_func("rb_enc_interned_str_cstr")

# Functions defined in 1.9 but not 1.8
have_func("rb_proc_arity")

Expand Down
18 changes: 16 additions & 2 deletions ext/sqlite3/statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,21 @@ column_count(VALUE self)
return INT2NUM(sqlite3_column_count(ctx->st));
}

#if HAVE_RB_ENC_INTERNED_STR_CSTR
static VALUE
interned_utf8_cstr(const char *str)
{
return rb_enc_interned_str_cstr(str, rb_utf8_encoding());
}
#else
static VALUE
interned_utf8_cstr(const char *str)
{
VALUE rb_str = rb_utf8_str_new_cstr(str);
return rb_funcall(rb_str, rb_intern("-@"), 0);
}
#endif

/* call-seq: stmt.column_name(index)
*
* Get the column name at +index+. 0 based.
Expand All @@ -382,8 +397,7 @@ column_name(VALUE self, VALUE index)
VALUE ret = Qnil;

if (name) {
ret = SQLITE3_UTF8_STR_NEW2(name);
rb_obj_freeze(ret);
ret = interned_utf8_cstr(name);
}
return ret;
}
Expand Down
17 changes: 17 additions & 0 deletions test/test_statement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ def test_raises_type_error
end
end

def test_column_names_are_deduped
@db.execute "CREATE TABLE 'things' ('float' float, 'int' int, 'text' blob, 'string' string, 'nil' string)"
stmt = @db.prepare "SELECT float, int, text, string, nil FROM things"
assert_equal ["float", "int", "text", "string", "nil"], stmt.columns
columns = stmt.columns
stmt.close

stmt = @db.prepare "SELECT float, int, text, string, nil FROM things"
# Make sure this new statement returns the same interned strings
stmt.columns.each_with_index do |str, i|
assert_predicate columns[i], :frozen?
assert_same columns[i], str
end
ensure
stmt&.close
end

def test_insert_duplicate_records
@db.execute 'CREATE TABLE "things" ("name" varchar(20) CONSTRAINT "index_things_on_name" UNIQUE)'
stmt = @db.prepare("INSERT INTO things(name) VALUES(?)")
Expand Down

0 comments on commit ea0cb80

Please sign in to comment.