diff --git a/ext/sqlite3/database.c b/ext/sqlite3/database.c index a35ff3a1..49a6e2cc 100644 --- a/ext/sqlite3/database.c +++ b/ext/sqlite3/database.c @@ -528,7 +528,7 @@ define_function_with_flags(VALUE self, VALUE name, VALUE flags) CHECK(ctx->db, status); - rb_hash_aset(rb_iv_get(self, "@functions"), name, block); + rb_ary_push(rb_iv_get(self, "@functions"), block); return self; } diff --git a/lib/sqlite3/database.rb b/lib/sqlite3/database.rb index 29314f7f..b75a6e03 100644 --- a/lib/sqlite3/database.rb +++ b/lib/sqlite3/database.rb @@ -175,7 +175,7 @@ def initialize file, options = {}, zvfs = nil @authorizer = nil @progress_handler = nil @collations = {} - @functions = {} + @functions = [] @results_as_hash = options[:results_as_hash] @readonly = mode & Constants::Open::READONLY != 0 @default_transaction_mode = options[:default_transaction_mode] || :deferred @@ -398,6 +398,8 @@ def get_first_value(sql, *bind_vars) # the FunctionProxy#result= method on the +func+ parameter and # indicate the return value that way. # + # A reference to the block will be kept for the lifetime of the database object. + # # Example: # # db.create_function( "maim", 1 ) do |func, value| diff --git a/test/test_database.rb b/test/test_database.rb index 3140a6a1..4b02ee6e 100644 --- a/test/test_database.rb +++ b/test/test_database.rb @@ -463,6 +463,14 @@ def test_define_function assert_equal 10, called_with end + def test_redefine_function_with_different_arity_does_not_use_freed_block + @db.define_function("f") { |a| "orig" } + @db.define_function("f") { |a, b| "new" } + GC.start(full_mark: true, immediate_sweep: true) + + assert_equal ["orig"], @db.execute("select f(1)").first + end + def test_call_func_arg_type called_with = nil @db.define_function("hello") do |b, c, d|