sequel 3.28.0 → 3.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/CHANGELOG +119 -3
  2. data/Rakefile +5 -3
  3. data/bin/sequel +1 -5
  4. data/doc/model_hooks.rdoc +9 -1
  5. data/doc/opening_databases.rdoc +49 -40
  6. data/doc/prepared_statements.rdoc +27 -6
  7. data/doc/release_notes/3.28.0.txt +2 -2
  8. data/doc/release_notes/3.29.0.txt +459 -0
  9. data/doc/sharding.rdoc +7 -1
  10. data/doc/testing.rdoc +18 -9
  11. data/doc/transactions.rdoc +41 -1
  12. data/lib/sequel/adapters/ado.rb +28 -17
  13. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  14. data/lib/sequel/adapters/amalgalite.rb +11 -7
  15. data/lib/sequel/adapters/db2.rb +122 -70
  16. data/lib/sequel/adapters/dbi.rb +15 -15
  17. data/lib/sequel/adapters/do.rb +5 -36
  18. data/lib/sequel/adapters/do/mysql.rb +0 -5
  19. data/lib/sequel/adapters/do/postgres.rb +0 -5
  20. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  21. data/lib/sequel/adapters/firebird.rb +3 -6
  22. data/lib/sequel/adapters/ibmdb.rb +24 -16
  23. data/lib/sequel/adapters/informix.rb +2 -4
  24. data/lib/sequel/adapters/jdbc.rb +47 -11
  25. data/lib/sequel/adapters/jdbc/as400.rb +5 -24
  26. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  28. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/h2.rb +10 -12
  30. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  31. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  32. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
  34. data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
  36. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  38. data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
  39. data/lib/sequel/adapters/mock.rb +315 -0
  40. data/lib/sequel/adapters/mysql.rb +64 -51
  41. data/lib/sequel/adapters/mysql2.rb +15 -9
  42. data/lib/sequel/adapters/odbc.rb +13 -6
  43. data/lib/sequel/adapters/odbc/db2.rb +0 -4
  44. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  45. data/lib/sequel/adapters/openbase.rb +2 -4
  46. data/lib/sequel/adapters/oracle.rb +333 -51
  47. data/lib/sequel/adapters/postgres.rb +80 -27
  48. data/lib/sequel/adapters/shared/access.rb +0 -6
  49. data/lib/sequel/adapters/shared/db2.rb +13 -15
  50. data/lib/sequel/adapters/shared/firebird.rb +6 -6
  51. data/lib/sequel/adapters/shared/mssql.rb +23 -18
  52. data/lib/sequel/adapters/shared/mysql.rb +6 -6
  53. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  54. data/lib/sequel/adapters/shared/oracle.rb +185 -30
  55. data/lib/sequel/adapters/shared/postgres.rb +35 -18
  56. data/lib/sequel/adapters/shared/progress.rb +0 -6
  57. data/lib/sequel/adapters/shared/sqlite.rb +116 -37
  58. data/lib/sequel/adapters/sqlite.rb +16 -8
  59. data/lib/sequel/adapters/swift.rb +5 -5
  60. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  61. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  62. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  63. data/lib/sequel/adapters/tinytds.rb +13 -10
  64. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
  65. data/lib/sequel/core.rb +40 -0
  66. data/lib/sequel/database/connecting.rb +1 -2
  67. data/lib/sequel/database/dataset.rb +3 -3
  68. data/lib/sequel/database/dataset_defaults.rb +58 -0
  69. data/lib/sequel/database/misc.rb +62 -2
  70. data/lib/sequel/database/query.rb +113 -49
  71. data/lib/sequel/database/schema_methods.rb +7 -2
  72. data/lib/sequel/dataset/actions.rb +37 -19
  73. data/lib/sequel/dataset/features.rb +24 -0
  74. data/lib/sequel/dataset/graph.rb +7 -6
  75. data/lib/sequel/dataset/misc.rb +11 -3
  76. data/lib/sequel/dataset/mutation.rb +2 -3
  77. data/lib/sequel/dataset/prepared_statements.rb +6 -4
  78. data/lib/sequel/dataset/query.rb +46 -15
  79. data/lib/sequel/dataset/sql.rb +28 -4
  80. data/lib/sequel/extensions/named_timezones.rb +5 -0
  81. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  82. data/lib/sequel/model.rb +2 -1
  83. data/lib/sequel/model/associations.rb +115 -33
  84. data/lib/sequel/model/base.rb +91 -31
  85. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  86. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  87. data/lib/sequel/plugins/force_encoding.rb +6 -6
  88. data/lib/sequel/plugins/identity_map.rb +1 -1
  89. data/lib/sequel/plugins/many_through_many.rb +6 -10
  90. data/lib/sequel/plugins/prepared_statements.rb +12 -1
  91. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
  92. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  93. data/lib/sequel/plugins/serialization.rb +6 -1
  94. data/lib/sequel/plugins/sharding.rb +0 -5
  95. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  96. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  97. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  98. data/lib/sequel/timezones.rb +42 -42
  99. data/lib/sequel/version.rb +1 -1
  100. data/spec/adapters/mssql_spec.rb +29 -29
  101. data/spec/adapters/mysql_spec.rb +86 -104
  102. data/spec/adapters/oracle_spec.rb +48 -76
  103. data/spec/adapters/postgres_spec.rb +98 -33
  104. data/spec/adapters/spec_helper.rb +0 -5
  105. data/spec/adapters/sqlite_spec.rb +24 -21
  106. data/spec/core/connection_pool_spec.rb +9 -15
  107. data/spec/core/core_sql_spec.rb +20 -31
  108. data/spec/core/database_spec.rb +491 -227
  109. data/spec/core/dataset_spec.rb +638 -1051
  110. data/spec/core/expression_filters_spec.rb +0 -1
  111. data/spec/core/mock_adapter_spec.rb +378 -0
  112. data/spec/core/object_graph_spec.rb +48 -114
  113. data/spec/core/schema_generator_spec.rb +3 -3
  114. data/spec/core/schema_spec.rb +51 -114
  115. data/spec/core/spec_helper.rb +3 -90
  116. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  117. data/spec/extensions/dataset_associations_spec.rb +199 -0
  118. data/spec/extensions/instance_hooks_spec.rb +71 -0
  119. data/spec/extensions/named_timezones_spec.rb +22 -2
  120. data/spec/extensions/nested_attributes_spec.rb +3 -0
  121. data/spec/extensions/schema_spec.rb +1 -1
  122. data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
  123. data/spec/extensions/serialization_spec.rb +5 -8
  124. data/spec/extensions/spec_helper.rb +4 -0
  125. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  126. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  127. data/spec/integration/associations_test.rb +123 -12
  128. data/spec/integration/dataset_test.rb +140 -47
  129. data/spec/integration/eager_loader_test.rb +19 -21
  130. data/spec/integration/model_test.rb +80 -1
  131. data/spec/integration/plugin_test.rb +179 -128
  132. data/spec/integration/prepared_statement_test.rb +92 -91
  133. data/spec/integration/schema_test.rb +42 -23
  134. data/spec/integration/spec_helper.rb +25 -31
  135. data/spec/integration/timezone_test.rb +38 -12
  136. data/spec/integration/transaction_test.rb +161 -34
  137. data/spec/integration/type_test.rb +3 -3
  138. data/spec/model/association_reflection_spec.rb +83 -7
  139. data/spec/model/associations_spec.rb +393 -676
  140. data/spec/model/base_spec.rb +186 -116
  141. data/spec/model/dataset_methods_spec.rb +7 -27
  142. data/spec/model/eager_loading_spec.rb +343 -867
  143. data/spec/model/hooks_spec.rb +160 -79
  144. data/spec/model/model_spec.rb +118 -165
  145. data/spec/model/plugins_spec.rb +7 -13
  146. data/spec/model/record_spec.rb +138 -207
  147. data/spec/model/spec_helper.rb +10 -73
  148. metadata +14 -8
@@ -1,36 +1,26 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
- require "timeout"
3
2
 
4
3
  unless defined?(ORACLE_DB)
5
4
  ORACLE_DB = Sequel.connect('oracle://hr:hr@localhost/XE')
6
5
  end
7
6
  INTEGRATION_DB = ORACLE_DB unless defined?(INTEGRATION_DB)
8
7
 
9
- if ORACLE_DB.table_exists?(:items)
10
- ORACLE_DB.drop_table :items
11
- end
12
- ORACLE_DB.create_table :items do
13
- varchar2 :name, :size => 50
14
- number :value, :size => 38
15
- date :date_created
8
+ ORACLE_DB.create_table!(:items) do
9
+ String :name, :size => 50
10
+ Integer :value
11
+ Date :date_created
16
12
  index :value
17
13
  end
18
14
 
19
- if ORACLE_DB.table_exists?(:books)
20
- ORACLE_DB.drop_table :books
21
- end
22
- ORACLE_DB.create_table :books do
23
- number :id, :size => 38
24
- varchar2 :title, :size => 50
25
- number :category_id, :size => 38
15
+ ORACLE_DB.create_table!(:books) do
16
+ Integer :id
17
+ String :title, :size => 50
18
+ Integer :category_id
26
19
  end
27
20
 
28
- if ORACLE_DB.table_exists?(:categories)
29
- ORACLE_DB.drop_table :categories
30
- end
31
- ORACLE_DB.create_table :categories do
32
- number :id, :size => 38
33
- varchar2 :cat_name, :size => 50
21
+ ORACLE_DB.create_table!(:categories) do
22
+ Integer :id
23
+ String :cat_name, :size => 50
34
24
  end
35
25
 
36
26
  describe "An Oracle database" do
@@ -40,38 +30,39 @@ describe "An Oracle database" do
40
30
  ORACLE_DB.disconnect
41
31
  ORACLE_DB.pool.size.should == 0
42
32
  end
33
+
34
+ specify "should be able to get current sequence value with SQL" do
35
+ begin
36
+ ORACLE_DB.create_table!(:foo){primary_key :id}
37
+ ORACLE_DB.fetch('SELECT seq_foo_id.nextval FROM DUAL').single_value.should == 1
38
+ ensure
39
+ ORACLE_DB.drop_table(:foo)
40
+ end
41
+ end
43
42
 
44
43
  specify "should provide schema information" do
45
- books_schema = [
46
- [:id, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}],
47
- [:title, {:char_size=>50, :type=>:varchar2, :allow_null=>true, :type_string=>"VARCHAR2(50)", :data_size=>50, :precision=>0, :char_used=>false, :scale=>0, :charset_form=>:implicit, :fsprecision=>0, :lfprecision=>0, :db_type=>"VARCHAR2(50)"}],
48
- [:category_id, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}]]
49
- categories_schema = [
50
- [:id, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}],
51
- [:cat_name, {:char_size=>50, :type=>:varchar2, :allow_null=>true, :type_string=>"VARCHAR2(50)", :data_size=>50, :precision=>0, :char_used=>false, :scale=>0, :charset_form=>:implicit, :fsprecision=>0, :lfprecision=>0, :db_type=>"VARCHAR2(50)"}]]
52
- items_schema = [
53
- [:name, {:char_size=>50, :type=>:varchar2, :allow_null=>true, :type_string=>"VARCHAR2(50)", :data_size=>50, :precision=>0, :char_used=>false, :scale=>0, :charset_form=>:implicit, :fsprecision=>0, :lfprecision=>0, :db_type=>"VARCHAR2(50)"}],
54
- [:value, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}],
55
- [:date_created, {:charset_form=>nil, :type=>:date, :type_string=>"DATE", :fsprecision=>0, :data_size=>7, :lfprecision=>0, :precision=>0, :db_type=>"DATE", :char_used=>false, :char_size=>0, :scale=>0, :allow_null=>true}]]
44
+ books_schema = [[:id, [:integer, false, true, nil]],
45
+ [:title, [:string, false, true, nil]],
46
+ [:category_id, [:integer, false, true, nil]]]
47
+ categories_schema = [[:id, [:integer, false, true, nil]],
48
+ [:cat_name, [:string, false, true, nil]]]
49
+ items_schema = [[:name, [:string, false, true, nil]],
50
+ [:value, [:integer, false, true, nil]],
51
+ [:date_created, [:datetime, false, true, nil]]]
56
52
 
57
53
  {:books => books_schema, :categories => categories_schema, :items => items_schema}.each_pair do |table, expected_schema|
58
54
  schema = ORACLE_DB.schema(table)
59
55
  schema.should_not be_nil
60
- expected_schema.should == schema
56
+ schema.map{|c, s| [c, s.values_at(:type, :primary_key, :allow_null, :ruby_default)]}.should == expected_schema
61
57
  end
62
58
  end
63
59
 
64
60
  specify "should create a temporary table" do
65
- ORACLE_DB.create_table :test_tmp, :temporary => true do
61
+ ORACLE_DB.create_table! :test_tmp, :temp => true do
62
+ varchar2 :name, :size => 50
66
63
  primary_key :id, :integer, :null => false
67
- column :name, :text
68
64
  index :name, :unique => true
69
65
  end
70
-
71
- ORACLE_DB.sqls.should == [
72
- 'CREATE GLOBAL TEMPORARY TABLE test_tmp (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
73
- 'CREATE UNIQUE INDEX test_tmp_name_index ON test_tmp (name)'
74
- ]
75
66
  end
76
67
  end
77
68
 
@@ -148,27 +139,27 @@ describe "An Oracle dataset" do
148
139
 
149
140
  @d.max(:value).to_i.should == 789
150
141
 
151
- @d.select(:name, :AVG.sql_function(:value)).filter(:name => 'abc').group(:name).to_a.should == [
152
- {:name => 'abc', :"avg(value)" => (456+123)/2.0}
142
+ @d.select(:name, :AVG.sql_function(:value).as(:avg)).filter(:name => 'abc').group(:name).to_a.should == [
143
+ {:name => 'abc', :avg => (456+123)/2.0}
153
144
  ]
154
145
 
155
- @d.select(:AVG.sql_function(:value)).group(:name).order(:name).limit(1).to_a.should == [
156
- {:"avg(value)" => (456+123)/2.0}
146
+ @d.select(:AVG.sql_function(:value).as(:avg)).group(:name).order(:name).limit(1).to_a.should == [
147
+ {:avg => (456+123)/2.0}
157
148
  ]
158
149
 
159
- @d.select(:name, :AVG.sql_function(:value)).group(:name).order(:name).to_a.should == [
160
- {:name => 'abc', :"avg(value)" => (456+123)/2.0},
161
- {:name => 'def', :"avg(value)" => 789*1.0}
150
+ @d.select(:name, :AVG.sql_function(:value).as(:avg)).group(:name).order(:name).to_a.should == [
151
+ {:name => 'abc', :avg => (456+123)/2.0},
152
+ {:name => 'def', :avg => 789*1.0}
162
153
  ]
163
154
 
164
- @d.select(:name, :AVG.sql_function(:value)).group(:name).order(:name).to_a.should == [
165
- {:name => 'abc', :"avg(value)" => (456+123)/2.0},
166
- {:name => 'def', :"avg(value)" => 789*1.0}
155
+ @d.select(:name, :AVG.sql_function(:value).as(:avg)).group(:name).order(:name).to_a.should == [
156
+ {:name => 'abc', :avg => (456+123)/2.0},
157
+ {:name => 'def', :avg => 789*1.0}
167
158
  ]
168
159
 
169
- @d.select(:name, :AVG.sql_function(:value)).group(:name).having(:name => ['abc', 'def']).order(:name).to_a.should == [
170
- {:name => 'abc', :"avg(value)" => (456+123)/2.0},
171
- {:name => 'def', :"avg(value)" => 789*1.0}
160
+ @d.select(:name, :AVG.sql_function(:value).as(:avg)).group(:name).having(:name => ['abc', 'def']).order(:name).to_a.should == [
161
+ {:name => 'abc', :avg => (456+123)/2.0},
162
+ {:name => 'def', :avg => 789*1.0}
172
163
  ]
173
164
 
174
165
  @d.select(:name, :value).filter(:name => 'abc').union(@d.select(:name, :value).filter(:name => 'def')).order(:value).to_a.should == [
@@ -194,7 +185,7 @@ describe "An Oracle dataset" do
194
185
  specify "should translate values correctly" do
195
186
  @d << {:name => 'abc', :value => 456}
196
187
  @d << {:name => 'def', :value => 789}
197
- @d.filter(:value > 500).update(:date_created => "to_timestamp('2009-09-09', 'YYYY-MM-DD')".lit)
188
+ @d.filter('value > 500').update(:date_created => "to_timestamp('2009-09-09', 'YYYY-MM-DD')".lit)
198
189
 
199
190
  @d[:name => 'def'][:date_created].should == Time.parse('2009-09-09')
200
191
  end
@@ -289,34 +280,15 @@ end
289
280
  describe "Row locks in Oracle" do
290
281
  before do
291
282
  @d1 = ORACLE_DB[:books]
292
- @d1.delete # remove all records
283
+ @d1.delete
293
284
  @d1 << {:id => 1, :title => 'aaa'}
294
285
  end
295
286
 
296
287
  specify "#for_update should use FOR UPDATE" do
297
- @d1.for_update.sql.should == "SELECT * FROM BOOKS FOR UPDATE"
288
+ @d1.for_update.sql.should == 'SELECT * FROM "BOOKS" FOR UPDATE'
298
289
  end
299
290
 
300
291
  specify "#lock_style should accept symbols" do
301
- @d1.lock_style(:update).sql.should == "SELECT * FROM BOOKS FOR UPDATE"
302
- end
303
-
304
- specify "should not update during row lock" do
305
- ORACLE_DB.transaction do
306
- @d1.filter(:id => 1).for_update.to_a
307
- proc do
308
- t1 = Thread.start do
309
- # wait for unlock
310
- Timeout::timeout(0.02) do
311
- ORACLE_DB[:books].filter(:id => 1).update(:title => "bbb")
312
- end
313
- end
314
- t1.join
315
- end.should raise_error
316
- @d1.filter(:id => 1).first[:title].should == "aaa"
317
- end
318
- t2 = Thread.start { ORACLE_DB[:books].filter(:id => 1).update(:title => "bbb") }
319
- t2.join
320
- @d1.filter(:id => 1).first[:title].should == "bbb"
292
+ @d1.lock_style(:update).sql.should == 'SELECT * FROM "BOOKS" FOR UPDATE'
321
293
  end
322
294
  end
@@ -197,35 +197,39 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
197
197
 
198
198
  specify "should handle FOR UPDATE" do
199
199
  @ds.insert(:number=>20)
200
- c = nil
201
- t = nil
200
+ c, t = nil, nil
201
+ q = Queue.new
202
202
  POSTGRES_DB.transaction do
203
203
  @ds.for_update.first(:id=>1)
204
204
  t = Thread.new do
205
205
  POSTGRES_DB.transaction do
206
+ q.push nil
206
207
  @ds.filter(:id=>1).update(:name=>'Jim')
207
208
  c = @ds.first(:id=>1)
209
+ q.push nil
208
210
  end
209
211
  end
210
- sleep 0.01
212
+ q.pop
211
213
  @ds.filter(:id=>1).update(:number=>30)
212
214
  end
215
+ q.pop
213
216
  t.join
214
217
  c.should == {:id=>1, :number=>30, :name=>'Jim'}
215
218
  end
216
219
 
217
220
  specify "should handle FOR SHARE" do
218
221
  @ds.insert(:number=>20)
219
- c = nil
220
- t = nil
222
+ c, t = nil
223
+ q = Queue.new
221
224
  POSTGRES_DB.transaction do
222
225
  @ds.for_share.first(:id=>1)
223
226
  t = Thread.new do
224
227
  POSTGRES_DB.transaction do
225
228
  c = @ds.for_share.filter(:id=>1).first
229
+ q.push nil
226
230
  end
227
231
  end
228
- sleep 0.1
232
+ q.pop
229
233
  @ds.filter(:id=>1).update(:name=>'Jim')
230
234
  c.should == {:id=>1, :number=>20, :name=>nil}
231
235
  end
@@ -351,17 +355,17 @@ describe "A PostgreSQL database" do
351
355
  specify "should support opclass specification" do
352
356
  @db.create_table(:posts){text :title; text :body; integer :user_id; index(:user_id, :opclass => :int4_ops, :type => :btree)}
353
357
  @db.sqls.should == [
354
- "CREATE TABLE posts (title text, body text, user_id integer)",
355
- "CREATE INDEX posts_user_id_index ON posts USING btree (user_id int4_ops)"
358
+ 'CREATE TABLE "posts" ("title" text, "body" text, "user_id" integer)',
359
+ 'CREATE INDEX "posts_user_id_index" ON "posts" USING btree ("user_id" int4_ops)'
356
360
  ]
357
361
  end
358
362
 
359
363
  specify "should support fulltext indexes and searching" do
360
364
  @db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french'}
361
365
  @db.sqls.should == [
362
- "CREATE TABLE posts (title text, body text)",
363
- "CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector('simple', (COALESCE(title, '') || ' ' || COALESCE(body, ''))))",
364
- "CREATE INDEX posts_title_index ON posts USING gin (to_tsvector('french', (COALESCE(title, ''))))"
366
+ %{CREATE TABLE "posts" ("title" text, "body" text)},
367
+ %{CREATE INDEX "posts_title_body_index" ON "posts" USING gin (to_tsvector('simple', (COALESCE("title", '') || ' ' || COALESCE("body", ''))))},
368
+ %{CREATE INDEX "posts_title_index" ON "posts" USING gin (to_tsvector('french', (COALESCE("title", ''))))}
365
369
  ]
366
370
 
367
371
  @db[:posts].insert(:title=>'ruby rails', :body=>'yowsa')
@@ -373,48 +377,48 @@ describe "A PostgreSQL database" do
373
377
  @db[:posts].full_text_search([:title, :body], ['yowsa', 'rails']).all.should == [:title=>'ruby rails', :body=>'yowsa']
374
378
  @db[:posts].full_text_search(:title, 'scooby', :language => 'french').all.should == [{:title=>'ruby scooby', :body=>'x'}]
375
379
  @db.sqls.should == [
376
- "SELECT * FROM posts WHERE (to_tsvector('simple', (COALESCE(title, ''))) @@ to_tsquery('simple', 'rails'))",
377
- "SELECT * FROM posts WHERE (to_tsvector('simple', (COALESCE(title, '') || ' ' || COALESCE(body, ''))) @@ to_tsquery('simple', 'yowsa | rails'))",
378
- "SELECT * FROM posts WHERE (to_tsvector('french', (COALESCE(title, ''))) @@ to_tsquery('french', 'scooby'))"]
380
+ %{SELECT * FROM "posts" WHERE (to_tsvector('simple', (COALESCE("title", ''))) @@ to_tsquery('simple', 'rails'))},
381
+ %{SELECT * FROM "posts" WHERE (to_tsvector('simple', (COALESCE("title", '') || ' ' || COALESCE("body", ''))) @@ to_tsquery('simple', 'yowsa | rails'))},
382
+ %{SELECT * FROM "posts" WHERE (to_tsvector('french', (COALESCE("title", ''))) @@ to_tsquery('french', 'scooby'))}]
379
383
  end
380
384
 
381
385
  specify "should support spatial indexes" do
382
386
  @db.create_table(:posts){box :geom; spatial_index [:geom]}
383
387
  @db.sqls.should == [
384
- "CREATE TABLE posts (geom box)",
385
- "CREATE INDEX posts_geom_index ON posts USING gist (geom)"
388
+ 'CREATE TABLE "posts" ("geom" box)',
389
+ 'CREATE INDEX "posts_geom_index" ON "posts" USING gist ("geom")'
386
390
  ]
387
391
  end
388
392
 
389
393
  specify "should support indexes with index type" do
390
394
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'hash'}
391
395
  @db.sqls.should == [
392
- "CREATE TABLE posts (title varchar(5))",
393
- "CREATE INDEX posts_title_index ON posts USING hash (title)"
396
+ 'CREATE TABLE "posts" ("title" varchar(5))',
397
+ 'CREATE INDEX "posts_title_index" ON "posts" USING hash ("title")'
394
398
  ]
395
399
  end
396
400
 
397
401
  specify "should support unique indexes with index type" do
398
402
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'btree', :unique => true}
399
403
  @db.sqls.should == [
400
- "CREATE TABLE posts (title varchar(5))",
401
- "CREATE UNIQUE INDEX posts_title_index ON posts USING btree (title)"
404
+ 'CREATE TABLE "posts" ("title" varchar(5))',
405
+ 'CREATE UNIQUE INDEX "posts_title_index" ON "posts" USING btree ("title")'
402
406
  ]
403
407
  end
404
408
 
405
409
  specify "should support partial indexes" do
406
410
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
407
411
  @db.sqls.should == [
408
- "CREATE TABLE posts (title varchar(5))",
409
- "CREATE INDEX posts_title_index ON posts (title) WHERE (title = '5')"
412
+ 'CREATE TABLE "posts" ("title" varchar(5))',
413
+ 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
410
414
  ]
411
415
  end
412
416
 
413
417
  specify "should support identifiers for table names in indicies" do
414
418
  @db.create_table(Sequel::SQL::Identifier.new(:posts)){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
415
419
  @db.sqls.should == [
416
- "CREATE TABLE posts (title varchar(5))",
417
- "CREATE INDEX posts_title_index ON posts (title) WHERE (title = '5')"
420
+ 'CREATE TABLE "posts" ("title" varchar(5))',
421
+ 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
418
422
  ]
419
423
  end
420
424
 
@@ -442,8 +446,8 @@ describe "Postgres::Dataset#import" do
442
446
 
443
447
  @db.sqls.should == [
444
448
  'BEGIN',
445
- 'INSERT INTO test (x, y) VALUES (1, 2)',
446
- 'INSERT INTO test (x, y) VALUES (3, 4)',
449
+ 'INSERT INTO "test" ("x", "y") VALUES (1, 2)',
450
+ 'INSERT INTO "test" ("x", "y") VALUES (3, 4)',
447
451
  'COMMIT'
448
452
  ]
449
453
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
@@ -456,7 +460,7 @@ describe "Postgres::Dataset#import" do
456
460
 
457
461
  @db.sqls.should == [
458
462
  'BEGIN',
459
- 'INSERT INTO test (x, y) VALUES (1, 2), (3, 4)',
463
+ 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)',
460
464
  'COMMIT'
461
465
  ]
462
466
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
@@ -487,10 +491,10 @@ describe "Postgres::Dataset#insert" do
487
491
  @ds.insert(:value=>13).should == 3
488
492
 
489
493
  @db.sqls.reject{|x| x =~ /pg_class/}.should == [
490
- 'INSERT INTO test5 (value) VALUES (10) RETURNING xid',
491
- 'INSERT INTO test5 (value) VALUES (20)',
494
+ 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"',
495
+ 'INSERT INTO "test5" ("value") VALUES (20)',
492
496
  "SELECT currval('\"public\".test5_xid_seq')",
493
- 'INSERT INTO test5 (value) VALUES (13)',
497
+ 'INSERT INTO "test5" ("value") VALUES (13)',
494
498
  "SELECT currval('\"public\".test5_xid_seq')"
495
499
  ]
496
500
  @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
@@ -498,20 +502,20 @@ describe "Postgres::Dataset#insert" do
498
502
 
499
503
  specify "should call execute_insert if server_version < 80200" do
500
504
  @ds.meta_def(:server_version){80100}
501
- @ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
505
+ @ds.should_receive(:execute_insert).once.with('INSERT INTO "test5" ("value") VALUES (10)', :table=>:test5, :values=>{:value=>10})
502
506
  @ds.insert(:value=>10)
503
507
  end
504
508
 
505
509
  specify "should call execute_insert if disabling insert returning" do
506
510
  @ds.disable_insert_returning!
507
- @ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
511
+ @ds.should_receive(:execute_insert).once.with('INSERT INTO "test5" ("value") VALUES (10)', :table=>:test5, :values=>{:value=>10})
508
512
  @ds.insert(:value=>10)
509
513
  end
510
514
 
511
515
  specify "should use INSERT RETURNING if server_version >= 80200" do
512
516
  @ds.meta_def(:server_version){80201}
513
517
  @ds.insert(:value=>10)
514
- @db.sqls.last.should == 'INSERT INTO test5 (value) VALUES (10) RETURNING xid'
518
+ @db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"'
515
519
  end
516
520
 
517
521
  specify "should have insert_select return nil if server_version < 80200" do
@@ -1093,4 +1097,65 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1093
1097
  @db[:test_copy].select_order_map(:x).should == [1, 3]
1094
1098
  end
1095
1099
  end
1100
+
1101
+ describe "Postgres::Database LISTEN/NOTIFY" do
1102
+ before(:all) do
1103
+ @db = POSTGRES_DB
1104
+ end
1105
+
1106
+ specify "should support listen and notify" do
1107
+ notify_pid = @db.synchronize{|conn| conn.backend_pid}
1108
+
1109
+ called = false
1110
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo')}) do |ev, pid, payload|
1111
+ ev.should == 'foo'
1112
+ pid.should == notify_pid
1113
+ ['', nil].should include(payload)
1114
+ called = true
1115
+ end.should == 'foo'
1116
+ called.should be_true
1117
+
1118
+ called = false
1119
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo', :payload=>'bar')}) do |ev, pid, payload|
1120
+ ev.should == 'foo'
1121
+ pid.should == notify_pid
1122
+ payload.should == 'bar'
1123
+ called = true
1124
+ end.should == 'foo'
1125
+ called.should be_true
1126
+
1127
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo')}).should == 'foo'
1128
+
1129
+ called = false
1130
+ called2 = false
1131
+ i = 0
1132
+ @db.listen(['foo', 'bar'], :after_listen=>proc{@db.notify('foo', :payload=>'bar'); @db.notify('bar', :payload=>'foo')}, :loop=>proc{i+=1}) do |ev, pid, payload|
1133
+ if !called
1134
+ ev.should == 'foo'
1135
+ pid.should == notify_pid
1136
+ payload.should == 'bar'
1137
+ called = true
1138
+ else
1139
+ ev.should == 'bar'
1140
+ pid.should == notify_pid
1141
+ payload.should == 'foo'
1142
+ called2 = true
1143
+ break
1144
+ end
1145
+ end.should be_nil
1146
+ called.should be_true
1147
+ called2.should be_true
1148
+ i.should == 1
1149
+ end
1150
+
1151
+ specify "should accept a :timeout option in listen" do
1152
+ @db.listen('foo2', :timeout=>0.001).should == nil
1153
+ called = false
1154
+ @db.listen('foo2', :timeout=>0.001){|ev, pid, payload| called = true}.should == nil
1155
+ called.should be_false
1156
+ i = 0
1157
+ @db.listen('foo2', :timeout=>0.001, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.should == nil
1158
+ i.should == 4
1159
+ end
1160
+ end
1096
1161
  end
@@ -3,7 +3,6 @@ require 'logger'
3
3
  unless Object.const_defined?('Sequel')
4
4
  $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
5
5
  require 'sequel'
6
- Sequel.quote_identifiers = false
7
6
  end
8
7
  begin
9
8
  require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'spec_config.rb')
@@ -31,10 +30,6 @@ end
31
30
  end
32
31
  end
33
32
 
34
- def self.log_specify(message, &block)
35
- specify(message){log{instance_eval(&block)}}
36
- end
37
-
38
33
  def self.cspecify(message, *checked, &block)
39
34
  return specify(message, &block) if ENV['SEQUEL_NO_PENDING']
40
35
  pending = false