sequel 4.42.1 → 4.43.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +35 -1
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/doc/release_notes/4.43.0.txt +87 -0
  6. data/doc/sql.rdoc +26 -27
  7. data/doc/testing.rdoc +2 -0
  8. data/doc/validations.rdoc +1 -1
  9. data/lib/sequel/adapters/ado.rb +5 -0
  10. data/lib/sequel/adapters/cubrid.rb +5 -0
  11. data/lib/sequel/adapters/ibmdb.rb +5 -0
  12. data/lib/sequel/adapters/jdbc.rb +6 -0
  13. data/lib/sequel/adapters/jdbc/derby.rb +5 -0
  14. data/lib/sequel/adapters/jdbc/hsqldb.rb +9 -5
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +1 -1
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
  17. data/lib/sequel/adapters/jdbc/transactions.rb +5 -0
  18. data/lib/sequel/adapters/mock.rb +12 -9
  19. data/lib/sequel/adapters/mysql.rb +6 -0
  20. data/lib/sequel/adapters/mysql2.rb +7 -2
  21. data/lib/sequel/adapters/oracle.rb +5 -0
  22. data/lib/sequel/adapters/shared/db2.rb +7 -1
  23. data/lib/sequel/adapters/shared/mssql.rb +5 -0
  24. data/lib/sequel/adapters/shared/mysql.rb +8 -1
  25. data/lib/sequel/adapters/shared/oracle.rb +20 -12
  26. data/lib/sequel/adapters/shared/postgres.rb +11 -2
  27. data/lib/sequel/adapters/shared/sqlanywhere.rb +6 -0
  28. data/lib/sequel/adapters/shared/sqlite.rb +29 -0
  29. data/lib/sequel/adapters/sqlanywhere.rb +5 -0
  30. data/lib/sequel/adapters/sqlite.rb +13 -0
  31. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  32. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -0
  33. data/lib/sequel/connection_pool/single.rb +15 -6
  34. data/lib/sequel/database/dataset.rb +3 -0
  35. data/lib/sequel/database/misc.rb +22 -1
  36. data/lib/sequel/database/query.rb +2 -4
  37. data/lib/sequel/dataset/actions.rb +0 -1
  38. data/lib/sequel/dataset/misc.rb +2 -4
  39. data/lib/sequel/dataset/query.rb +23 -6
  40. data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
  41. data/lib/sequel/extensions/_model_pg_row.rb +47 -0
  42. data/lib/sequel/extensions/looser_typecasting.rb +2 -0
  43. data/lib/sequel/extensions/migration.rb +12 -1
  44. data/lib/sequel/extensions/pg_array.rb +6 -0
  45. data/lib/sequel/extensions/pg_enum.rb +2 -1
  46. data/lib/sequel/extensions/pg_range.rb +6 -0
  47. data/lib/sequel/extensions/pg_row.rb +8 -0
  48. data/lib/sequel/model/associations.rb +3 -1
  49. data/lib/sequel/model/base.rb +14 -3
  50. data/lib/sequel/plugins/constraint_validations.rb +1 -8
  51. data/lib/sequel/plugins/instance_filters.rb +1 -1
  52. data/lib/sequel/plugins/pg_row.rb +1 -40
  53. data/lib/sequel/plugins/prepared_statements.rb +51 -20
  54. data/lib/sequel/plugins/prepared_statements_associations.rb +22 -4
  55. data/lib/sequel/plugins/prepared_statements_with_pk.rb +9 -1
  56. data/lib/sequel/plugins/sharding.rb +5 -0
  57. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  58. data/lib/sequel/version.rb +2 -2
  59. data/spec/adapters/spec_helper.rb +4 -0
  60. data/spec/core/connection_pool_spec.rb +10 -0
  61. data/spec/core/database_spec.rb +29 -0
  62. data/spec/extensions/blacklist_security_spec.rb +4 -4
  63. data/spec/extensions/defaults_setter_spec.rb +1 -1
  64. data/spec/extensions/force_encoding_spec.rb +3 -2
  65. data/spec/extensions/identifier_mangling_spec.rb +7 -0
  66. data/spec/extensions/instance_filters_spec.rb +1 -0
  67. data/spec/extensions/migration_spec.rb +19 -0
  68. data/spec/extensions/pg_array_spec.rb +5 -0
  69. data/spec/extensions/pg_range_spec.rb +5 -0
  70. data/spec/extensions/pg_row_spec.rb +9 -0
  71. data/spec/extensions/prepared_statements_associations_spec.rb +45 -1
  72. data/spec/extensions/prepared_statements_spec.rb +138 -41
  73. data/spec/extensions/prepared_statements_with_pk_spec.rb +7 -0
  74. data/spec/extensions/serialization_spec.rb +6 -6
  75. data/spec/extensions/single_table_inheritance_spec.rb +3 -3
  76. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  77. data/spec/integration/associations_test.rb +2 -2
  78. data/spec/integration/dataset_test.rb +0 -4
  79. data/spec/integration/eager_loader_test.rb +5 -5
  80. data/spec/integration/plugin_test.rb +8 -6
  81. data/spec/integration/schema_test.rb +2 -2
  82. data/spec/integration/spec_helper.rb +10 -0
  83. data/spec/integration/timezone_test.rb +1 -1
  84. data/spec/integration/transaction_test.rb +5 -5
  85. data/spec/model/associations_spec.rb +13 -6
  86. data/spec/model/base_spec.rb +1 -1
  87. data/spec/model/hooks_spec.rb +4 -4
  88. data/spec/model/model_spec.rb +2 -2
  89. data/spec/model/record_spec.rb +17 -18
  90. metadata +6 -2
@@ -14,6 +14,13 @@ describe "identifier_mangling extension" do
14
14
  db.quote_identifiers?.must_equal true
15
15
  end
16
16
 
17
+ it "should respect the :quote_identifiers setting" do
18
+ db = Sequel::Database.new(:identifier_mangling=>true)
19
+ db.quote_identifiers?.must_equal false
20
+ db.quote_identifiers = true
21
+ db.quote_identifiers?.must_equal true
22
+ end
23
+
17
24
  it "should upcase on input and downcase on output by default" do
18
25
  db = Sequel::Database.new(:identifier_mangling=>true)
19
26
  db.send(:identifier_input_method_default).must_equal :upcase
@@ -39,6 +39,7 @@ describe "instance_filters plugin" do
39
39
 
40
40
  @p = @c.load(:id=>1, :name=>'John', :num=>1)
41
41
  @p.instance_variable_set(:@this, @p.this.with_numrows(1))
42
+ @c.instance_variable_set(:@fast_instance_delete_sql, nil)
42
43
  @p.destroy
43
44
  DB.sqls.must_equal ["DELETE FROM people WHERE (id = 1)"]
44
45
  @p.instance_filter(:name=>'Jim')
@@ -772,4 +772,23 @@ describe "Sequel::TimestampMigrator" do
772
772
  Sequel::TimestampMigrator.run(@db, "spec/files/transaction_unspecified_migrations", :use_transactions=>false)
773
773
  @db.sqls.must_equal ["SELECT NULL AS nil FROM schema_migrations LIMIT 1", "CREATE TABLE schema_migrations (filename varchar(255) PRIMARY KEY)", "SELECT NULL AS nil FROM schema_info LIMIT 1", "SELECT filename FROM schema_migrations ORDER BY filename", "CREATE TABLE sm11111 (smc1 integer)", "INSERT INTO schema_migrations (filename) VALUES ('001_create_alt_basic.rb')", "CREATE TABLE sm (smc1 integer)", "INSERT INTO schema_migrations (filename) VALUES ('002_create_basic.rb')"]
774
774
  end
775
+
776
+ it "should use shorter primary key field on MySQL if creating schema migrations table fails" do
777
+ def @db.database_type; :mysql end
778
+ def @db.execute_ddl(sql, *)
779
+ super
780
+ raise Sequel::DatabaseError, "Specified key was too long; max key length is 767 bytes" if sql =~ /varchar\(255\)/
781
+ end
782
+ Sequel::TimestampMigrator.run(@db, "spec/files/transaction_unspecified_migrations", :use_transactions=>false)
783
+ @db.sqls.must_equal ["SELECT NULL AS nil FROM schema_migrations LIMIT 1", "CREATE TABLE schema_migrations (filename varchar(255) PRIMARY KEY)", "CREATE TABLE schema_migrations (filename varchar(190) PRIMARY KEY)", "SELECT NULL AS nil FROM schema_info LIMIT 1", "SELECT filename FROM schema_migrations ORDER BY filename", "CREATE TABLE sm11111 (smc1 integer)", "INSERT INTO schema_migrations (filename) VALUES ('001_create_alt_basic.rb')", "CREATE TABLE sm (smc1 integer)", "INSERT INTO schema_migrations (filename) VALUES ('002_create_basic.rb')"]
784
+ end
785
+
786
+ it "should not use shorter primary key field on other databases if creating schema migrations table fails" do
787
+ def @db.execute_ddl(sql, *)
788
+ super
789
+ raise Sequel::DatabaseError, "Specified key was too long; max key length is 767 bytes" if sql =~ /varchar\(255\)/
790
+ end
791
+ proc{Sequel::TimestampMigrator.run(@db, "spec/files/transaction_unspecified_migrations", :use_transactions=>false)}.must_raise Sequel::DatabaseError
792
+ @db.sqls.must_equal ["SELECT NULL AS nil FROM schema_migrations LIMIT 1", "CREATE TABLE schema_migrations (filename varchar(255) PRIMARY KEY)"]
793
+ end
775
794
  end
@@ -330,6 +330,11 @@ describe "pg_array extension" do
330
330
  @db.literal(Sequel::Postgres::PG_TYPES[3].call('{}')).must_equal "'{}'::blah[]"
331
331
  end
332
332
 
333
+ it "should not support registering custom array types on a per-Database basis for frozen databases" do
334
+ @db.freeze
335
+ proc{@db.register_array_type('banana', :oid=>7865){|s| s}}.must_raise RuntimeError, TypeError
336
+ end
337
+
333
338
  it "should support registering custom array types on a per-Database basis" do
334
339
  @db.register_array_type('banana', :oid=>7865){|s| s}
335
340
  @db.typecast_value(:banana_array, []).class.must_equal(Sequel::Postgres::PGArray)
@@ -170,6 +170,11 @@ describe "pg_range extension" do
170
170
  Sequel::Postgres::PG_TYPES[331].call('[1,3)').must_be_kind_of(@R)
171
171
  end
172
172
 
173
+ it "should not support registering custom range types on a per-Database basis for frozen databases" do
174
+ @db.freeze
175
+ proc{@db.register_range_type('banana', :oid=>7865){|s| s}}.must_raise RuntimeError, TypeError
176
+ end
177
+
173
178
  it "should support registering custom range types on a per-Database basis" do
174
179
  @db.register_range_type('banana', :oid=>7865){|s| s}
175
180
  @db.typecast_value(:banana, '[1,2]').class.must_equal(Sequel::Postgres::PGRange)
@@ -231,6 +231,15 @@ describe "pg_row extension" do
231
231
  @db.literal(p1.call('(1,b)')).must_equal "ROW(1, 'bb')::foo.bar"
232
232
  end
233
233
 
234
+ it "should not allow registering on a frozen database" do
235
+ @db.conversion_procs[4] = proc{|s| s.to_i}
236
+ @db.conversion_procs[5] = proc{|s| s * 2}
237
+ @db.fetch = [[], [{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
238
+ c = proc{|h| [h]}
239
+ @db.freeze
240
+ proc{@db.register_row_type(:foo, :converter=>c)}.must_raise RuntimeError, TypeError
241
+ end
242
+
234
243
  it "should allow registering with a custom converter" do
235
244
  @db.conversion_procs[4] = proc{|s| s.to_i}
236
245
  @db.conversion_procs[5] = proc{|s| s * 2}
@@ -2,7 +2,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "Sequel::Plugins::PreparedStatementsAssociations" do
4
4
  before do
5
- @db = Sequel.mock
5
+ @db = Sequel.mock(:servers=>{:foo=>{}})
6
6
  @db.extend_datasets do
7
7
  def select_sql
8
8
  sql = super
@@ -52,6 +52,43 @@ describe "Sequel::Plugins::PreparedStatementsAssociations" do
52
52
  @db.sqls.must_equal ["SELECT tags.id, tags.id2 FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) WHERE (albums.artist_id = 1) LIMIT 1 -- prepared"]
53
53
  end
54
54
 
55
+ it "should run correct shard for associations when also using sharding plugin" do
56
+ @Artist.plugin :sharding
57
+ @Album.plugin :sharding
58
+
59
+ @Artist.load(:id=>1).set_server(:foo).albums
60
+ @db.sqls.must_equal ["SELECT id, artist_id, id2, artist_id2 FROM albums WHERE (albums.artist_id = 1) -- prepared -- foo"]
61
+
62
+ @Artist.load(:id=>1).set_server(:foo).album
63
+ @db.sqls.must_equal ["SELECT id, artist_id, id2, artist_id2 FROM albums WHERE (albums.artist_id = 1) LIMIT 1 -- prepared -- foo"]
64
+
65
+ @Album.load(:id=>1, :artist_id=>2).set_server(:foo).artist
66
+ @db.sqls.must_equal ["SELECT id, id2 FROM artists WHERE (artists.id = 2) LIMIT 1 -- prepared -- foo"]
67
+
68
+ @Album.load(:id=>1, :artist_id=>2).set_server(:foo).tags
69
+ @db.sqls.must_equal ["SELECT tags.id, tags.id2 FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE (albums_tags.album_id = 1) -- prepared -- foo"]
70
+
71
+ @Album.load(:id=>1, :artist_id=>2).set_server(:foo).tag
72
+ @db.sqls.must_equal ["SELECT tags.id, tags.id2 FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE (albums_tags.album_id = 1) LIMIT 1 -- prepared -- foo"]
73
+
74
+ @Artist.load(:id=>1).set_server(:foo).tags
75
+ @db.sqls.must_equal ["SELECT tags.id, tags.id2 FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) WHERE (albums.artist_id = 1) -- prepared -- foo"]
76
+
77
+ @Artist.load(:id=>1).set_server(:foo).tag
78
+ @db.sqls.must_equal ["SELECT tags.id, tags.id2 FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) WHERE (albums.artist_id = 1) LIMIT 1 -- prepared -- foo"]
79
+
80
+ @Tag.plugin :sharding
81
+ @Tag.plugin :prepared_statements_associations
82
+ @Tag.many_to_many :albums, :class=>@Album, :join_table=>:albums_tags, :left_key=>:tag_id
83
+ @Tag.load(:id=>1).set_server(:foo).albums
84
+ @db.sqls.must_equal ["SELECT albums.id, albums.artist_id, albums.id2, albums.artist_id2 FROM albums INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE (albums_tags.tag_id = 1) -- prepared -- foo"]
85
+ end
86
+
87
+ it "should not override the shard for associations if not using the sharding plugin" do
88
+ @Artist.load(:id=>1).set_server(:foo).albums
89
+ @db.sqls.must_equal ["SELECT id, artist_id, id2, artist_id2 FROM albums WHERE (albums.artist_id = 1) -- prepared"]
90
+ end
91
+
55
92
  it "should run correct SQL for composite key associations" do
56
93
  @Artist.one_to_many :albums, :class=>@Album, :key=>[:artist_id, :artist_id2], :primary_key=>[:id, :id2]
57
94
  @Artist.one_to_one :album, :class=>@Album, :key=>[:artist_id, :artist_id2], :primary_key=>[:id, :id2]
@@ -156,4 +193,11 @@ describe "Sequel::Plugins::PreparedStatementsAssociations" do
156
193
  @Artist.load(:id=>1).oalbums
157
194
  @db.sqls.must_equal ["SELECT * FROM albums WHERE (albums.artist_id = 1)"]
158
195
  end
196
+
197
+ it "should work correctly when using an instance specific association" do
198
+ tag = @Tag
199
+ @Artist.many_to_one :tag, :key=>nil, :read_only=>true, :dataset=>proc{tag.where(:id=>id).limit(1)}, :reciprocal=>nil, :reciprocal_type=>nil
200
+ @Artist.load(:id=>1).tag.must_be_nil
201
+ @db.sqls.must_equal ["SELECT * FROM tags WHERE (id = 1) LIMIT 1"]
202
+ end
159
203
  end
@@ -5,6 +5,7 @@ describe "prepared_statements plugin" do
5
5
  @db = Sequel.mock(:fetch=>{:id=>1, :name=>'foo', :i=>2}, :autoid=>proc{|sql| 1}, :numrows=>1, :servers=>{:read_only=>{}})
6
6
  @c = Class.new(Sequel::Model(@db[:people]))
7
7
  @c.columns :id, :name, :i
8
+ @c.set_primary_key :id
8
9
  @columns = "id, name, i"
9
10
  @c.plugin :prepared_statements
10
11
  @p = @c.load(:id=>1, :name=>'foo', :i=>2)
@@ -12,11 +13,6 @@ describe "prepared_statements plugin" do
12
13
  @db.sqls
13
14
  end
14
15
 
15
- it "should correctly lookup by primary key" do
16
- @c[1].must_equal @p
17
- @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
18
- end
19
-
20
16
  it "should correctly lookup by primary key for joined dataset" do
21
17
  @c.dataset = @c.dataset.from(:people, :people2)
22
18
  @db.sqls
@@ -24,22 +20,46 @@ describe "prepared_statements plugin" do
24
20
  @db.sqls.must_equal ["SELECT * FROM people, people2 WHERE (people.id = 1) LIMIT 1 -- read_only"]
25
21
  end
26
22
 
27
- prepared_statements_spec = shared_description do
28
- it "should correctly delete instance" do
29
- @p.destroy.must_equal @p
30
- @db.sqls.must_equal ["DELETE FROM people WHERE (id = 1)"]
31
- end
23
+ it "should use prepared statements for pk lookups only if default is not optimized" do
24
+ @c.send(:use_prepared_statements_for_pk_lookup?).must_equal false
25
+ @c.set_primary_key [:id, :name]
26
+ @c.send(:use_prepared_statements_for_pk_lookup?).must_equal true
27
+ @c.set_primary_key :id
28
+ @c.dataset = @c.dataset.from(:people, :people2)
29
+ @c.send(:use_prepared_statements_for_pk_lookup?).must_equal false
30
+ @c.dataset = @db[:people].select(:id, :name, :i)
31
+ @c.send(:use_prepared_statements_for_pk_lookup?).must_equal true
32
+ end
33
+
34
+ it "should use prepared statements for refreshes if default is not optimized" do
35
+ @p.send(:use_prepared_statements_for?, :refresh).must_equal false
36
+ @c.set_primary_key [:id, :name]
37
+ @p.send(:use_prepared_statements_for?, :refresh).must_equal true
38
+ end
39
+
40
+ it "should use prepared statements for deletes if default is not optimized" do
41
+ @p.send(:use_prepared_statements_for?, :delete).must_equal false
42
+ @c.set_primary_key [:id, :name]
43
+ @p.send(:use_prepared_statements_for?, :delete).must_equal true
44
+ end
32
45
 
46
+ it "should use prepared statements for deletes if default on Oracle and DB2" do
47
+ def @db.database_type; :oracle end
48
+ @p.send(:use_prepared_statements_for?, :delete).must_equal true
49
+ def @db.database_type; :db2 end
50
+ @p.send(:use_prepared_statements_for?, :delete).must_equal true
51
+ end
52
+
53
+ it "should raise Error for unsupported prepared statement types" do
54
+ proc{@p.send(:use_prepared_statements_for?, :foo)}.must_raise Sequel::Error
55
+ end
56
+
57
+ prepared_statements_spec = shared_description do
33
58
  it "should correctly update instance" do
34
59
  @p.update(:name=>'bar').must_equal @c.load(:id=>1, :name=>'bar', :i => 2)
35
60
  @db.sqls.must_equal ["UPDATE people SET name = 'bar' WHERE (id = 1)"]
36
61
  end
37
62
 
38
- it "should correctly create instance" do
39
- @c.create(:name=>'foo').must_equal @c.load(:id=>1, :name=>'foo', :i => 2)
40
- @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo')", "SELECT #{@columns} FROM people WHERE (id = 1) LIMIT 1"]
41
- end
42
-
43
63
  it "should correctly create instance if dataset supports insert_select" do
44
64
  @c.dataset_module do
45
65
  def supports_insert_select?
@@ -61,51 +81,128 @@ describe "prepared_statements plugin" do
61
81
  end
62
82
  end
63
83
 
64
- describe "when #use_prepared_statements_for? returns false" do
84
+ describe "when #use_prepared_statements_for? returns true" do
65
85
  before do
66
- @columns = "*"
67
- @c.class_eval{def use_prepared_statements_for?(type) false end}
86
+ @c.class_eval do
87
+ def self.use_prepared_statements_for_pk_lookup?; true end
88
+ def use_prepared_statements_for?(type) true end
89
+ end
68
90
  end
69
91
 
70
92
  include prepared_statements_spec
71
- end
72
93
 
73
- include prepared_statements_spec
94
+ it "should correctly create instance" do
95
+ @c.create(:name=>'foo').must_equal @c.load(:id=>1, :name=>'foo', :i => 2)
96
+ @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo')", "SELECT #{@columns} FROM people WHERE (id = 1) LIMIT 1"]
97
+ end
74
98
 
75
- it "should work correctly when subclassing" do
76
- c = Class.new(@c)
77
- c[1].must_equal c.load(:id=>1, :name=>'foo', :i=>2)
78
- @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
79
- end
99
+ it "should correctly lookup by primary key" do
100
+ @c[1].must_equal @p
101
+ @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
102
+ end
80
103
 
81
- describe " with placeholder type specifiers" do
82
- before do
83
- @ds = @ds.with_extend{def requires_placeholder_type_specifiers?; true end}
104
+ it "should correctly delete instance" do
105
+ @p.destroy.must_equal @p
106
+ @db.sqls.must_equal ["DELETE FROM people WHERE (id = 1)"]
107
+ end
108
+
109
+ it "should correctly delete instance when specifying server" do
110
+ @p.set_server(:read_only).destroy.must_equal @p
111
+ @db.sqls.must_equal ["DELETE FROM people WHERE (id = 1) -- read_only"]
112
+ end
113
+
114
+ it "should correctly update instance when specifying server" do
115
+ @p.set_server(:read_only).update(:name=>'bar').must_equal @c.load(:id=>1, :name=>'bar', :i => 2)
116
+ @db.sqls.must_equal ["UPDATE people SET name = 'bar' WHERE (id = 1) -- read_only"]
84
117
  end
85
118
 
86
- it "should correctly handle without schema type" do
119
+ it "should correctly create instance when specifying server" do
120
+ @c.new(:name=>'foo').set_server(:read_only).save.must_equal @c.load(:id=>1, :name=>'foo', :i => 2)
121
+ @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo') -- read_only", "SELECT #{@columns} FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
122
+ end
123
+
124
+ it "should correctly create instance if dataset supports insert_select when specifying server" do
125
+ @c.dataset_module do
126
+ def supports_insert_select?
127
+ true
128
+ end
129
+ def supports_returning?(type)
130
+ true
131
+ end
132
+ def insert_select(h)
133
+ cache_set(:_fetch, :id=>1, :name=>'foo', :i => 2)
134
+ server(:default).with_sql_first(insert_select_sql(h))
135
+ end
136
+ def insert_select_sql(*v)
137
+ "#{insert_sql(*v)} RETURNING #{(opts[:returning] && !opts[:returning].empty?) ? opts[:returning].map{|c| literal(c)}.join(', ') : '*'}"
138
+ end
139
+ end
140
+ @c.new(:name=>'foo').set_server(:read_only).save.must_equal @c.load(:id=>1, :name=>'foo', :i => 2)
141
+ @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo') RETURNING #{@columns} -- read_only"]
142
+ end
143
+
144
+ it "should work correctly when subclassing" do
145
+ c = Class.new(@c)
146
+ c[1].must_equal c.load(:id=>1, :name=>'foo', :i=>2)
147
+ @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
148
+ end
149
+
150
+ it "should correctly handle without schema type when placeholder type specifiers are required" do
151
+ @c.dataset = @ds.with_extend{def requires_placeholder_type_specifiers?; true end}
87
152
  @c[1].must_equal @p
88
153
  @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
89
154
  end
90
155
 
91
- it "should correctly handle with schema type" do
92
- @c.db_schema[:id][:type] = :integer
93
- ds = @c.send(:prepared_lookup).with_extend do
94
- def literal_symbol_append(sql, v)
95
- if @opts[:bind_vars] and match = /\A\$(.*)\z/.match(v.to_s)
96
- s = match[1].split('__')[0].to_sym
97
- if prepared_arg?(s)
98
- literal_append(sql, prepared_arg(s))
99
- else
100
- sql << v.to_s
156
+ it "should correctly handle with schema type when placeholder type specifiers are required" do
157
+ @c.dataset = @ds.with_extend do
158
+ def requires_placeholder_type_specifiers?; true end
159
+ def prepare(*)
160
+ super.with_extend do
161
+ def literal_symbol_append(sql, v)
162
+ if @opts[:bind_vars] && (match = /\A\$(.*)\z/.match(v.to_s))
163
+ s = match[1].split('__')[0].to_sym
164
+ if prepared_arg?(s)
165
+ literal_append(sql, prepared_arg(s))
166
+ else
167
+ sql << v.to_s
168
+ end
169
+ else
170
+ super
171
+ end
101
172
  end
102
- else
103
- super
104
173
  end
105
174
  end
106
175
  end
176
+ @c.db_schema[:id][:type] = :integer
107
177
  @c[1].must_equal @p
108
178
  @db.sqls.must_equal ["SELECT id, name, i FROM people WHERE (id = 1) LIMIT 1 -- read_only"]
109
179
  end
110
180
  end
181
+
182
+ describe "when #use_prepared_statements_for? returns false" do
183
+ before do
184
+ @columns = "*"
185
+ @c.class_eval do
186
+ def self.use_prepared_statements_for_pk_lookup?; false end
187
+ def use_prepared_statements_for?(type) false end
188
+ end
189
+ end
190
+
191
+ include prepared_statements_spec
192
+
193
+ it "should correctly create instance" do
194
+ @c.create(:name=>'foo').must_equal @c.load(:id=>1, :name=>'foo', :i => 2)
195
+ @db.sqls.must_equal ["INSERT INTO people (name) VALUES ('foo')", "SELECT #{@columns} FROM people WHERE id = 1"]
196
+ end
197
+
198
+ it "should correctly lookup by primary key" do
199
+ @c[1].must_equal @p
200
+ @db.sqls.must_equal ["SELECT * FROM people WHERE id = 1 -- read_only"]
201
+ end
202
+
203
+ it "should correctly delete instance" do
204
+ @p.destroy.must_equal @p
205
+ @db.sqls.must_equal ["DELETE FROM people WHERE id = 1"]
206
+ end
207
+ end
111
208
  end
@@ -28,4 +28,11 @@ describe "prepared_statements_with_pk plugin" do
28
28
  @c.dataset.filter{id > 2}[1].must_equal @p
29
29
  @c.db.sqls.must_equal ["SELECT * FROM people WHERE ((id > 2) AND (people.id = 1)) LIMIT 1 -- read_only"]
30
30
  end
31
+
32
+ it "should respect explicitly set server" do
33
+ @c.dataset.filter(:name=>'foo')[1].must_equal @p
34
+ @c.db.sqls.must_equal ["SELECT * FROM people WHERE ((name = 'foo') AND (people.id = 1)) LIMIT 1 -- read_only"]
35
+ @c.dataset.server(:default).filter(:name=>'foo')[1].must_equal @p
36
+ @c.db.sqls.must_equal ["SELECT * FROM people WHERE ((name = 'foo') AND (people.id = 1)) LIMIT 1"]
37
+ end
31
38
  end
@@ -108,7 +108,7 @@ describe "Serialization plugin" do
108
108
  DB.sqls.must_equal ["SELECT * FROM items LIMIT 1",
109
109
  "UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
110
110
  "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')",
111
- "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
111
+ "SELECT * FROM items WHERE id = 10"]
112
112
  end
113
113
 
114
114
  it "should translate values to and from marshal serialization format using accessor methods" do
@@ -128,7 +128,7 @@ describe "Serialization plugin" do
128
128
  DB.sqls.must_equal ["SELECT * FROM items LIMIT 1",
129
129
  "UPDATE items SET abc = '#{[Marshal.dump(23)].pack('m')}' WHERE (id = 1)",
130
130
  "INSERT INTO items (abc) VALUES ('#{[Marshal.dump([1, 2, 3])].pack('m')}')",
131
- "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
131
+ "SELECT * FROM items WHERE id = 10"]
132
132
  end
133
133
 
134
134
  it "should handle old non-base-64 encoded marshal serialization format" do
@@ -169,7 +169,7 @@ describe "Serialization plugin" do
169
169
  DB.sqls.must_equal ["SELECT * FROM items LIMIT 1",
170
170
  "UPDATE items SET abc = '#{[23].to_json}' WHERE (id = 1)",
171
171
  "INSERT INTO items (abc) VALUES ('#{[1,2,3].to_json}')",
172
- "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
172
+ "SELECT * FROM items WHERE id = 10"]
173
173
  end
174
174
 
175
175
  it "should translate values to and from arbitrary callables using accessor methods" do
@@ -190,7 +190,7 @@ describe "Serialization plugin" do
190
190
  DB.sqls.must_equal ["SELECT * FROM items LIMIT 1",
191
191
  "UPDATE items SET abc = 'oof' WHERE (id = 1)",
192
192
  "INSERT INTO items (abc) VALUES ('rab')",
193
- "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
193
+ "SELECT * FROM items WHERE id = 10"]
194
194
  end
195
195
 
196
196
  it "should handle registration of custom serializer/deserializer pairs" do
@@ -213,7 +213,7 @@ describe "Serialization plugin" do
213
213
  DB.sqls.must_equal ["SELECT * FROM items LIMIT 1",
214
214
  "UPDATE items SET abc = 'oof' WHERE (id = 1)",
215
215
  "INSERT INTO items (abc) VALUES ('rab')",
216
- "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
216
+ "SELECT * FROM items WHERE id = 10"]
217
217
  end
218
218
 
219
219
  it "should copy serialization formats and columns to subclasses" do
@@ -233,7 +233,7 @@ describe "Serialization plugin" do
233
233
  DB.sqls.must_equal ["SELECT * FROM items LIMIT 1",
234
234
  "UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)",
235
235
  "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')",
236
- "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
236
+ "SELECT * FROM items WHERE id = 10"]
237
237
  end
238
238
 
239
239
  it "should clear the deserialized columns when refreshing" do