spec/extensions/prepared_statements_spec.rb in sequel-4.42.1 vs spec/extensions/prepared_statements_spec.rb in sequel-4.43.0

- old
+ new

@@ -3,45 +3,65 @@ describe "prepared_statements plugin" do before do @db = Sequel.mock(:fetch=>{:id=>1, :name=>'foo', :i=>2}, :autoid=>proc{|sql| 1}, :numrows=>1, :servers=>{:read_only=>{}}) @c = Class.new(Sequel::Model(@db[:people])) @c.columns :id, :name, :i + @c.set_primary_key :id @columns = "id, name, i" @c.plugin :prepared_statements @p = @c.load(:id=>1, :name=>'foo', :i=>2) @ds = @c.dataset @db.sqls end - it "should correctly lookup by primary key" do - @c[1].must_equal @p - @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"] - end - it "should correctly lookup by primary key for joined dataset" do @c.dataset = @c.dataset.from(:people, :people2) @db.sqls @c[1].must_equal @p @db.sqls.must_equal ["SELECT * FROM people, people2 WHERE (people.id = 1) LIMIT 1 -- read_only"] end - prepared_statements_spec = shared_description do - it "should correctly delete instance" do - @p.destroy.must_equal @p - @db.sqls.must_equal ["DELETE FROM people WHERE (id = 1)"] - end + it "should use prepared statements for pk lookups only if default is not optimized" do + @c.send(:use_prepared_statements_for_pk_lookup?).must_equal false + @c.set_primary_key [:id, :name] + @c.send(:use_prepared_statements_for_pk_lookup?).must_equal true + @c.set_primary_key :id + @c.dataset = @c.dataset.from(:people, :people2) + @c.send(:use_prepared_statements_for_pk_lookup?).must_equal false + @c.dataset = @db[:people].select(:id, :name, :i) + @c.send(:use_prepared_statements_for_pk_lookup?).must_equal true + end + it "should use prepared statements for refreshes if default is not optimized" do + @p.send(:use_prepared_statements_for?, :refresh).must_equal false + @c.set_primary_key [:id, :name] + @p.send(:use_prepared_statements_for?, :refresh).must_equal true + end + + it "should use prepared statements for deletes if default is not optimized" do + @p.send(:use_prepared_statements_for?, :delete).must_equal false + @c.set_primary_key [:id, :name] + @p.send(:use_prepared_statements_for?, :delete).must_equal true + end + + it "should use prepared statements for deletes if default on Oracle and DB2" do + def @db.database_type; :oracle end + @p.send(:use_prepared_statements_for?, :delete).must_equal true + def @db.database_type; :db2 end + @p.send(:use_prepared_statements_for?, :delete).must_equal true + end + + it "should raise Error for unsupported prepared statement types" do + proc{@p.send(:use_prepared_statements_for?, :foo)}.must_raise Sequel::Error + end + + prepared_statements_spec = shared_description do it "should correctly update instance" do @p.update(:name=>'bar').must_equal @c.load(:id=>1, :name=>'bar', :i => 2) @db.sqls.must_equal ["UPDATE people SET name = 'bar' WHERE (id = 1)"] end - it "should correctly create instance" do - @c.create(:name=>'foo').must_equal @c.load(:id=>1, :name=>'foo', :i => 2) - @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo')", "SELECT #{@columns} FROM people WHERE (id = 1) LIMIT 1"] - end - it "should correctly create instance if dataset supports insert_select" do @c.dataset_module do def supports_insert_select? true end @@ -59,53 +79,130 @@ @c.create(:name=>'foo').must_equal @c.load(:id=>1, :name=>'foo', :i => 2) @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo') RETURNING #{@columns}"] end end - describe "when #use_prepared_statements_for? returns false" do + describe "when #use_prepared_statements_for? returns true" do before do - @columns = "*" - @c.class_eval{def use_prepared_statements_for?(type) false end} + @c.class_eval do + def self.use_prepared_statements_for_pk_lookup?; true end + def use_prepared_statements_for?(type) true end + end end include prepared_statements_spec - end - include prepared_statements_spec + it "should correctly create instance" do + @c.create(:name=>'foo').must_equal @c.load(:id=>1, :name=>'foo', :i => 2) + @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo')", "SELECT #{@columns} FROM people WHERE (id = 1) LIMIT 1"] + end - it "should work correctly when subclassing" do - c = Class.new(@c) - c[1].must_equal c.load(:id=>1, :name=>'foo', :i=>2) - @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"] - end + it "should correctly lookup by primary key" do + @c[1].must_equal @p + @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"] + end - describe " with placeholder type specifiers" do - before do - @ds = @ds.with_extend{def requires_placeholder_type_specifiers?; true end} + it "should correctly delete instance" do + @p.destroy.must_equal @p + @db.sqls.must_equal ["DELETE FROM people WHERE (id = 1)"] end - it "should correctly handle without schema type" do + it "should correctly delete instance when specifying server" do + @p.set_server(:read_only).destroy.must_equal @p + @db.sqls.must_equal ["DELETE FROM people WHERE (id = 1) -- read_only"] + end + + it "should correctly update instance when specifying server" do + @p.set_server(:read_only).update(:name=>'bar').must_equal @c.load(:id=>1, :name=>'bar', :i => 2) + @db.sqls.must_equal ["UPDATE people SET name = 'bar' WHERE (id = 1) -- read_only"] + end + + it "should correctly create instance when specifying server" do + @c.new(:name=>'foo').set_server(:read_only).save.must_equal @c.load(:id=>1, :name=>'foo', :i => 2) + @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo') -- read_only", "SELECT #{@columns} FROM people WHERE (id = 1) LIMIT 1 -- read_only"] + end + + it "should correctly create instance if dataset supports insert_select when specifying server" do + @c.dataset_module do + def supports_insert_select? + true + end + def supports_returning?(type) + true + end + def insert_select(h) + cache_set(:_fetch, :id=>1, :name=>'foo', :i => 2) + server(:default).with_sql_first(insert_select_sql(h)) + end + def insert_select_sql(*v) + "#{insert_sql(*v)} RETURNING #{(opts[:returning] && !opts[:returning].empty?) ? opts[:returning].map{|c| literal(c)}.join(', ') : '*'}" + end + end + @c.new(:name=>'foo').set_server(:read_only).save.must_equal @c.load(:id=>1, :name=>'foo', :i => 2) + @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo') RETURNING #{@columns} -- read_only"] + end + + it "should work correctly when subclassing" do + c = Class.new(@c) + c[1].must_equal c.load(:id=>1, :name=>'foo', :i=>2) + @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"] + end + + it "should correctly handle without schema type when placeholder type specifiers are required" do + @c.dataset = @ds.with_extend{def requires_placeholder_type_specifiers?; true end} @c[1].must_equal @p @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"] end - it "should correctly handle with schema type" do - @c.db_schema[:id][:type] = :integer - ds = @c.send(:prepared_lookup).with_extend do - def literal_symbol_append(sql, v) - if @opts[:bind_vars] and match = /\A\$(.*)\z/.match(v.to_s) - s = match[1].split('__')[0].to_sym - if prepared_arg?(s) - literal_append(sql, prepared_arg(s)) - else - sql << v.to_s + it "should correctly handle with schema type when placeholder type specifiers are required" do + @c.dataset = @ds.with_extend do + def requires_placeholder_type_specifiers?; true end + def prepare(*) + super.with_extend do + def literal_symbol_append(sql, v) + if @opts[:bind_vars] && (match = /\A\$(.*)\z/.match(v.to_s)) + s = match[1].split('__')[0].to_sym + if prepared_arg?(s) + literal_append(sql, prepared_arg(s)) + else + sql << v.to_s + end + else + super + end end - else - super end end end + @c.db_schema[:id][:type] = :integer @c[1].must_equal @p @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"] end + end + + describe "when #use_prepared_statements_for? returns false" do + before do + @columns = "*" + @c.class_eval do + def self.use_prepared_statements_for_pk_lookup?; false end + def use_prepared_statements_for?(type) false end + end + end + + include prepared_statements_spec + + it "should correctly create instance" do + @c.create(:name=>'foo').must_equal @c.load(:id=>1, :name=>'foo', :i => 2) + @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo')", "SELECT #{@columns} FROM people WHERE id = 1"] + end + + it "should correctly lookup by primary key" do + @c[1].must_equal @p + @db.sqls.must_equal ["SELECT * FROM people WHERE id = 1 -- read_only"] + end + + it "should correctly delete instance" do + @p.destroy.must_equal @p + @db.sqls.must_equal ["DELETE FROM people WHERE id = 1"] + end end end