sequel 4.42.1 → 4.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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