sequel 3.21.0 → 3.32.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 (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -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,51 @@ 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'))}]
383
+
384
+ @db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
385
+ @db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
379
386
  end
380
387
 
381
388
  specify "should support spatial indexes" do
382
389
  @db.create_table(:posts){box :geom; spatial_index [:geom]}
383
390
  @db.sqls.should == [
384
- "CREATE TABLE posts (geom box)",
385
- "CREATE INDEX posts_geom_index ON posts USING gist (geom)"
391
+ 'CREATE TABLE "posts" ("geom" box)',
392
+ 'CREATE INDEX "posts_geom_index" ON "posts" USING gist ("geom")'
386
393
  ]
387
394
  end
388
395
 
389
396
  specify "should support indexes with index type" do
390
397
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'hash'}
391
398
  @db.sqls.should == [
392
- "CREATE TABLE posts (title varchar(5))",
393
- "CREATE INDEX posts_title_index ON posts USING hash (title)"
399
+ 'CREATE TABLE "posts" ("title" varchar(5))',
400
+ 'CREATE INDEX "posts_title_index" ON "posts" USING hash ("title")'
394
401
  ]
395
402
  end
396
403
 
397
404
  specify "should support unique indexes with index type" do
398
405
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'btree', :unique => true}
399
406
  @db.sqls.should == [
400
- "CREATE TABLE posts (title varchar(5))",
401
- "CREATE UNIQUE INDEX posts_title_index ON posts USING btree (title)"
407
+ 'CREATE TABLE "posts" ("title" varchar(5))',
408
+ 'CREATE UNIQUE INDEX "posts_title_index" ON "posts" USING btree ("title")'
402
409
  ]
403
410
  end
404
411
 
405
412
  specify "should support partial indexes" do
406
413
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
407
414
  @db.sqls.should == [
408
- "CREATE TABLE posts (title varchar(5))",
409
- "CREATE INDEX posts_title_index ON posts (title) WHERE (title = '5')"
415
+ 'CREATE TABLE "posts" ("title" varchar(5))',
416
+ 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
410
417
  ]
411
418
  end
412
419
 
413
420
  specify "should support identifiers for table names in indicies" do
414
421
  @db.create_table(Sequel::SQL::Identifier.new(:posts)){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
415
422
  @db.sqls.should == [
416
- "CREATE TABLE posts (title varchar(5))",
417
- "CREATE INDEX posts_title_index ON posts (title) WHERE (title = '5')"
423
+ 'CREATE TABLE "posts" ("title" varchar(5))',
424
+ 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
418
425
  ]
419
426
  end
420
427
 
@@ -427,7 +434,7 @@ end
427
434
  describe "Postgres::Dataset#import" do
428
435
  before do
429
436
  @db = POSTGRES_DB
430
- @db.create_table!(:test){Integer :x; Integer :y}
437
+ @db.create_table!(:test){primary_key :x; Integer :y}
431
438
  @db.sqls.clear
432
439
  @ds = @db[:test]
433
440
  end
@@ -437,28 +444,45 @@ describe "Postgres::Dataset#import" do
437
444
 
438
445
  specify "#import should return separate insert statements if server_version < 80200" do
439
446
  @ds.meta_def(:server_version){80199}
440
-
441
447
  @ds.import([:x, :y], [[1, 2], [3, 4]])
442
-
443
- @db.sqls.should == [
444
- 'BEGIN',
445
- 'INSERT INTO test (x, y) VALUES (1, 2)',
446
- 'INSERT INTO test (x, y) VALUES (3, 4)',
447
- 'COMMIT'
448
- ]
448
+ @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2)', 'INSERT INTO "test" ("x", "y") VALUES (3, 4)', 'COMMIT']
449
449
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
450
450
  end
451
451
 
452
452
  specify "#import should a single insert statement if server_version >= 80200" do
453
453
  @ds.meta_def(:server_version){80200}
454
-
455
454
  @ds.import([:x, :y], [[1, 2], [3, 4]])
456
-
457
- @db.sqls.should == [
458
- 'BEGIN',
459
- 'INSERT INTO test (x, y) VALUES (1, 2), (3, 4)',
460
- 'COMMIT'
461
- ]
455
+ @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)', 'COMMIT']
456
+ @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
457
+ end
458
+
459
+ specify "#import should work correctly when returning primary keys for server_version < 80200" do
460
+ @ds.meta_def(:server_version){80199}
461
+ @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
462
+ @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
463
+ end
464
+
465
+ specify "#import should work correctly when returning primary keys for server_version >= 80200" do
466
+ @ds.meta_def(:server_version){80200}
467
+ @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
468
+ @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
469
+ end
470
+
471
+ specify "#import should work correctly when returning primary keys with :slice option for server_version < 80200" do
472
+ @ds.meta_def(:server_version){80199}
473
+ @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
474
+ @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
475
+ end
476
+
477
+ specify "#import should work correctly when returning primary keys with :slice option for server_version >= 80200" do
478
+ @ds.meta_def(:server_version){80200}
479
+ @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
480
+ @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
481
+ end
482
+
483
+ specify "#import should work correctly with an arbitrary returning value" do
484
+ @ds.meta_def(:server_version){80200}
485
+ @ds.returning(:y, :x).import([:x, :y], [[1, 2], [3, 4]]).should == [{:y=>2, :x=>1}, {:y=>4, :x=>3}]
462
486
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
463
487
  end
464
488
  end
@@ -487,36 +511,36 @@ describe "Postgres::Dataset#insert" do
487
511
  @ds.insert(:value=>13).should == 3
488
512
 
489
513
  @db.sqls.reject{|x| x =~ /pg_class/}.should == [
490
- 'INSERT INTO test5 (value) VALUES (10) RETURNING xid',
491
- 'INSERT INTO test5 (value) VALUES (20)',
514
+ 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"',
515
+ 'INSERT INTO "test5" ("value") VALUES (20)',
492
516
  "SELECT currval('\"public\".test5_xid_seq')",
493
- 'INSERT INTO test5 (value) VALUES (13)',
517
+ 'INSERT INTO "test5" ("value") VALUES (13)',
494
518
  "SELECT currval('\"public\".test5_xid_seq')"
495
519
  ]
496
520
  @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
497
521
  end
498
522
 
499
- specify "should call execute_insert if server_version < 80200" do
523
+ specify "should insert correctly if server_version < 80200" do
500
524
  @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})
502
- @ds.insert(:value=>10)
525
+ @ds.insert(:value=>10).should == 1
526
+ @ds.all.should == [{:xid=>1, :value=>10}]
503
527
  end
504
528
 
505
- specify "should call execute_insert if disabling insert returning" do
506
- @ds.disable_insert_returning!
507
- @ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
508
- @ds.insert(:value=>10)
529
+ specify "should insert correctly if disabling insert returning" do
530
+ @ds.disable_insert_returning.insert(:value=>10).should == 1
531
+ @ds.all.should == [{:xid=>1, :value=>10}]
509
532
  end
510
533
 
511
- specify "should use INSERT RETURNING if server_version >= 80200" do
512
- @ds.meta_def(:server_version){80201}
513
- @ds.insert(:value=>10)
514
- @db.sqls.last.should == 'INSERT INTO test5 (value) VALUES (10) RETURNING xid'
534
+ specify "should insert correctly if using a column array and a value array and server_version < 80200" do
535
+ @ds.meta_def(:server_version){80100}
536
+ @ds.insert([:value], [10]).should == 1
537
+ @ds.all.should == [{:xid=>1, :value=>10}]
515
538
  end
516
539
 
517
- specify "should have insert_returning_sql use the RETURNING keyword" do
518
- @ds.insert_returning_sql(:xid, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING xid"
519
- @ds.insert_returning_sql('*'.lit, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING *"
540
+ specify "should use INSERT RETURNING if server_version >= 80200" do
541
+ @ds.meta_def(:server_version){80201}
542
+ @ds.insert(:value=>10).should == 1
543
+ @db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"'
520
544
  end
521
545
 
522
546
  specify "should have insert_select return nil if server_version < 80200" do
@@ -606,10 +630,16 @@ describe "Postgres::Database schema qualified tables" do
606
630
  POSTGRES_DB.drop_table(:domains)
607
631
  end
608
632
 
609
- specify "#table_exists? should not include tables from the default non-public schemas" do
610
- POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
611
- POSTGRES_DB.table_exists?(:schema_test).should == true
612
- POSTGRES_DB.table_exists?(:domain_udt_usage).should == false
633
+ specify "#schema should raise an exception if columns from tables in two separate schema are returned" do
634
+ POSTGRES_DB.create_table!(:public__domains){integer :d}
635
+ POSTGRES_DB.create_table(:schema_test__domains){integer :i}
636
+ begin
637
+ proc{POSTGRES_DB.schema(:domains)}.should raise_error(Sequel::Error)
638
+ POSTGRES_DB.schema(:public__domains).map{|x| x.first}.should == [:d]
639
+ POSTGRES_DB.schema(:schema_test__domains).map{|x| x.first}.should == [:i]
640
+ ensure
641
+ POSTGRES_DB.drop_table(:public__domains)
642
+ end
613
643
  end
614
644
 
615
645
  specify "#table_exists? should see if the table is in a given schema" do
@@ -909,7 +939,7 @@ describe "Postgres::Database functions, languages, and triggers" do
909
939
  after do
910
940
  @d.drop_function('tf', :if_exists=>true, :cascade=>true)
911
941
  @d.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>%w'integer integer')
912
- @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
942
+ @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
913
943
  @d.drop_table(:test) rescue nil
914
944
  end
915
945
 
@@ -940,19 +970,19 @@ describe "Postgres::Database functions, languages, and triggers" do
940
970
 
941
971
  specify "#create_language and #drop_language should create and drop languages" do
942
972
  @d.send(:create_language_sql, :plpgsql).should == 'CREATE LANGUAGE plpgsql'
943
- @d.create_language(:plpgsql, :replace=>true)
973
+ @d.create_language(:plpgsql, :replace=>true) if @d.server_version < 90000
944
974
  proc{@d.create_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
945
975
  @d.send(:drop_language_sql, :plpgsql).should == 'DROP LANGUAGE plpgsql'
946
- @d.drop_language(:plpgsql)
947
- proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
976
+ @d.drop_language(:plpgsql) if @d.server_version < 90000
977
+ proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError) if @d.server_version < 90000
948
978
  @d.send(:create_language_sql, :plpgsql, :replace=>true, :trusted=>true, :handler=>:a, :validator=>:b).should == (@d.server_version >= 90000 ? 'CREATE OR REPLACE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b' : 'CREATE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b')
949
979
  @d.send(:drop_language_sql, :plpgsql, :if_exists=>true, :cascade=>true).should == 'DROP LANGUAGE IF EXISTS plpgsql CASCADE'
950
980
  # Make sure if exists works
951
- @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
981
+ @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
952
982
  end
953
983
 
954
984
  specify "#create_trigger and #drop_trigger should create and drop triggers" do
955
- @d.create_language(:plpgsql)
985
+ @d.create_language(:plpgsql) if @d.server_version < 90000
956
986
  @d.create_function(:tf, 'BEGIN IF NEW.value IS NULL THEN RAISE EXCEPTION \'Blah\'; END IF; RETURN NEW; END;', :language=>:plpgsql, :returns=>:trigger)
957
987
  @d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON test FOR EACH ROW EXECUTE PROCEDURE tf()'
958
988
  @d.create_table(:test){String :name; Integer :value}
@@ -973,17 +1003,17 @@ describe "Postgres::Database functions, languages, and triggers" do
973
1003
  end
974
1004
 
975
1005
  if POSTGRES_DB.adapter_scheme == :postgres
976
- describe "Postgres::Dataset #use_cursor" do
977
- before(:all) do
978
- @db = POSTGRES_DB
979
- @db.create_table!(:test_cursor){Integer :x}
980
- @db.sqls.clear
981
- @ds = @db[:test_cursor]
982
- @db.transaction{1001.times{|i| @ds.insert(i)}}
983
- end
984
- after(:all) do
985
- @db.drop_table(:test) rescue nil
986
- end
1006
+ describe "Postgres::Dataset #use_cursor" do
1007
+ before(:all) do
1008
+ @db = POSTGRES_DB
1009
+ @db.create_table!(:test_cursor){Integer :x}
1010
+ @db.sqls.clear
1011
+ @ds = @db[:test_cursor]
1012
+ @db.transaction{1001.times{|i| @ds.insert(i)}}
1013
+ end
1014
+ after(:all) do
1015
+ @db.drop_table(:test_cursor) rescue nil
1016
+ end
987
1017
 
988
1018
  specify "should return the same results as the non-cursor use" do
989
1019
  @ds.all.should == @ds.use_cursor.all
@@ -1005,5 +1035,164 @@ describe "Postgres::Dataset #use_cursor" do
1005
1035
  @ds.check_return
1006
1036
  @ds.all.should == @ds.use_cursor.all
1007
1037
  end
1038
+ end
1039
+
1040
+ describe "Postgres::PG_NAMED_TYPES" do
1041
+ before do
1042
+ @db = POSTGRES_DB
1043
+ Sequel::Postgres::PG_NAMED_TYPES[:interval] = lambda{|v| v.reverse}
1044
+ @db.instance_eval do
1045
+ disconnect
1046
+ @conversion_procs = nil
1047
+ end
1048
+ end
1049
+ after do
1050
+ Sequel::Postgres::PG_NAMED_TYPES.delete(:interval)
1051
+ @db.instance_eval do
1052
+ disconnect
1053
+ @conversion_procs = nil
1054
+ end
1055
+ @db.drop_table(:foo)
1056
+ end
1057
+
1058
+ specify "should look up conversion procs by name" do
1059
+ @db.create_table!(:foo){interval :bar}
1060
+ @db[:foo].insert('21 days')
1061
+ @db[:foo].get(:bar).should == 'syad 12'
1062
+ end
1063
+ end
1008
1064
  end
1065
+
1066
+ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRES_DB.server_version >= 90000
1067
+ describe "Postgres::Database#copy_table" do
1068
+ before(:all) do
1069
+ @db = POSTGRES_DB
1070
+ @db.create_table!(:test_copy){Integer :x; Integer :y}
1071
+ ds = @db[:test_copy]
1072
+ ds.insert(1, 2)
1073
+ ds.insert(3, 4)
1074
+ end
1075
+ after(:all) do
1076
+ @db.drop_table(:test_copy) rescue nil
1077
+ end
1078
+
1079
+ specify "without a block or options should return a text version of the table as a single string" do
1080
+ @db.copy_table(:test_copy).should == "1\t2\n3\t4\n"
1081
+ end
1082
+
1083
+ specify "without a block and with :format=>:csv should return a csv version of the table as a single string" do
1084
+ @db.copy_table(:test_copy, :format=>:csv).should == "1,2\n3,4\n"
1085
+ end
1086
+
1087
+ specify "should treat string as SQL code" do
1088
+ @db.copy_table('COPY "test_copy" TO STDOUT').should == "1\t2\n3\t4\n"
1089
+ end
1090
+
1091
+ specify "should respect given :options options" do
1092
+ @db.copy_table(:test_copy, :options=>"FORMAT csv, HEADER TRUE").should == "x,y\n1,2\n3,4\n"
1093
+ end
1094
+
1095
+ specify "should respect given :options options when :format is used" do
1096
+ @db.copy_table(:test_copy, :format=>:csv, :options=>"QUOTE '''', FORCE_QUOTE *").should == "'1','2'\n'3','4'\n"
1097
+ end
1098
+
1099
+ specify "should accept dataset as first argument" do
1100
+ @db.copy_table(@db[:test_copy].cross_join(:test_copy___tc).order(1, 2, 3, 4)).should == "1\t2\t1\t2\n1\t2\t3\t4\n3\t4\t1\t2\n3\t4\t3\t4\n"
1101
+ end
1102
+
1103
+ specify "with a block and no options should yield each row as a string in text format" do
1104
+ buf = []
1105
+ @db.copy_table(:test_copy){|b| buf << b}
1106
+ buf.should == ["1\t2\n", "3\t4\n"]
1107
+ end
1108
+
1109
+ specify "with a block and :format=>:csv should yield each row as a string in csv format" do
1110
+ buf = []
1111
+ @db.copy_table(:test_copy, :format=>:csv){|b| buf << b}
1112
+ buf.should == ["1,2\n", "3,4\n"]
1113
+ end
1114
+
1115
+ specify "should work fine when using a block that is terminated early with a following copy_table" do
1116
+ buf = []
1117
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
1118
+ buf.should == ["1,2\n"]
1119
+ buf.clear
1120
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.should raise_error(Sequel::DatabaseDisconnectError)
1121
+ buf.should == ["1,2\n"]
1122
+ buf.clear
1123
+ @db.copy_table(:test_copy){|b| buf << b}
1124
+ buf.should == ["1\t2\n", "3\t4\n"]
1125
+ end
1126
+
1127
+ specify "should work fine when using a block that is terminated early with a following regular query" do
1128
+ buf = []
1129
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
1130
+ buf.should == ["1,2\n"]
1131
+ buf.clear
1132
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.should raise_error(Sequel::DatabaseDisconnectError)
1133
+ buf.should == ["1,2\n"]
1134
+ @db[:test_copy].select_order_map(:x).should == [1, 3]
1135
+ end
1136
+ end
1137
+
1138
+ describe "Postgres::Database LISTEN/NOTIFY" do
1139
+ before(:all) do
1140
+ @db = POSTGRES_DB
1141
+ end
1142
+
1143
+ specify "should support listen and notify" do
1144
+ notify_pid = @db.synchronize{|conn| conn.backend_pid}
1145
+
1146
+ called = false
1147
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo')}) do |ev, pid, payload|
1148
+ ev.should == 'foo'
1149
+ pid.should == notify_pid
1150
+ ['', nil].should include(payload)
1151
+ called = true
1152
+ end.should == 'foo'
1153
+ called.should be_true
1154
+
1155
+ called = false
1156
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo', :payload=>'bar')}) do |ev, pid, payload|
1157
+ ev.should == 'foo'
1158
+ pid.should == notify_pid
1159
+ payload.should == 'bar'
1160
+ called = true
1161
+ end.should == 'foo'
1162
+ called.should be_true
1163
+
1164
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo')}).should == 'foo'
1165
+
1166
+ called = false
1167
+ called2 = false
1168
+ i = 0
1169
+ @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|
1170
+ if !called
1171
+ ev.should == 'foo'
1172
+ pid.should == notify_pid
1173
+ payload.should == 'bar'
1174
+ called = true
1175
+ else
1176
+ ev.should == 'bar'
1177
+ pid.should == notify_pid
1178
+ payload.should == 'foo'
1179
+ called2 = true
1180
+ break
1181
+ end
1182
+ end.should be_nil
1183
+ called.should be_true
1184
+ called2.should be_true
1185
+ i.should == 1
1186
+ end
1187
+
1188
+ specify "should accept a :timeout option in listen" do
1189
+ @db.listen('foo2', :timeout=>0.001).should == nil
1190
+ called = false
1191
+ @db.listen('foo2', :timeout=>0.001){|ev, pid, payload| called = true}.should == nil
1192
+ called.should be_false
1193
+ i = 0
1194
+ @db.listen('foo2', :timeout=>0.001, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.should == nil
1195
+ i.should == 4
1196
+ end
1197
+ end
1009
1198
  end
@@ -3,13 +3,17 @@ 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')
10
9
  rescue LoadError
11
10
  end
12
11
 
12
+ if ENV['SEQUEL_COLUMNS_INTROSPECTION']
13
+ Sequel.extension :columns_introspection
14
+ Sequel::Dataset.introspect_all_columns
15
+ end
16
+
13
17
  class Sequel::Database
14
18
  def log_duration(duration, message)
15
19
  log_info(message)
@@ -26,11 +30,8 @@ end
26
30
  end
27
31
  end
28
32
 
29
- def self.log_specify(message, &block)
30
- specify(message){log{instance_eval(&block)}}
31
- end
32
-
33
33
  def self.cspecify(message, *checked, &block)
34
+ return specify(message, &block) if ENV['SEQUEL_NO_PENDING']
34
35
  pending = false
35
36
  checked.each do |c|
36
37
  case c
@@ -66,6 +66,17 @@ describe "An SQLite database" do
66
66
  @db[:fk].all.should == [{:id=>1, :parent_id=>2}]
67
67
  end
68
68
 
69
+ specify "should support a use_timestamp_timezones setting" do
70
+ @db.create_table!(:time){Time :time}
71
+ @db[:time].insert(Time.now)
72
+ @db[:time].get(:time.cast_string).should =~ /[-+]\d\d\d\d\z/
73
+ @db.use_timestamp_timezones = false
74
+ @db[:time].delete
75
+ @db[:time].insert(Time.now)
76
+ @db[:time].get(:time.cast_string).should_not =~ /[-+]\d\d\d\d\z/
77
+ @db.use_timestamp_timezones = true
78
+ end
79
+
69
80
  specify "should provide a list of existing tables" do
70
81
  @db.drop_table(:testing) rescue nil
71
82
  @db.tables.should be_a_kind_of(Array)
@@ -133,13 +144,6 @@ describe "An SQLite dataset" do
133
144
  @d = SQLITE_DB[:items]
134
145
  end
135
146
 
136
- specify "should handle string pattern matches correctly" do
137
- @d.literal(:x.like('a')).should == "(x LIKE 'a')"
138
- @d.literal(~:x.like('a')).should == "NOT (x LIKE 'a')"
139
- @d.literal(:x.ilike('a')).should == "(x LIKE 'a')"
140
- @d.literal(~:x.ilike('a')).should == "NOT (x LIKE 'a')"
141
- end
142
-
143
147
  specify "should raise errors if given a regexp pattern match" do
144
148
  proc{@d.literal(:x.like(/a/))}.should raise_error(Sequel::Error)
145
149
  proc{@d.literal(~:x.like(/a/))}.should raise_error(Sequel::Error)
@@ -164,27 +168,27 @@ end
164
168
 
165
169
  describe "An SQLite dataset AS clause" do
166
170
  specify "should use a string literal for :col___alias" do
167
- SQLITE_DB.literal(:c___a).should == "c AS 'a'"
171
+ SQLITE_DB.literal(:c___a).should == "`c` AS 'a'"
168
172
  end
169
173
 
170
174
  specify "should use a string literal for :table__col___alias" do
171
- SQLITE_DB.literal(:t__c___a).should == "t.c AS 'a'"
175
+ SQLITE_DB.literal(:t__c___a).should == "`t`.`c` AS 'a'"
172
176
  end
173
177
 
174
178
  specify "should use a string literal for :column.as(:alias)" do
175
- SQLITE_DB.literal(:c.as(:a)).should == "c AS 'a'"
179
+ SQLITE_DB.literal(:c.as(:a)).should == "`c` AS 'a'"
176
180
  end
177
181
 
178
182
  specify "should use a string literal in the SELECT clause" do
179
- SQLITE_DB[:t].select(:c___a).sql.should == "SELECT c AS 'a' FROM t"
183
+ SQLITE_DB[:t].select(:c___a).sql.should == "SELECT `c` AS 'a' FROM `t`"
180
184
  end
181
185
 
182
186
  specify "should use a string literal in the FROM clause" do
183
- SQLITE_DB[:t___a].sql.should == "SELECT * FROM t AS 'a'"
187
+ SQLITE_DB[:t___a].sql.should == "SELECT * FROM `t` AS 'a'"
184
188
  end
185
189
 
186
190
  specify "should use a string literal in the JOIN clause" do
187
- SQLITE_DB[:t].join_table(:natural, :j, nil, :a).sql.should == "SELECT * FROM t NATURAL JOIN j AS 'a'"
191
+ SQLITE_DB[:t].join_table(:natural, :j, nil, :a).sql.should == "SELECT * FROM `t` NATURAL JOIN `j` AS 'a'"
188
192
  end
189
193
  end
190
194
 
@@ -313,6 +317,15 @@ describe "A SQLite database" do
313
317
  @db[:test2].first.should == {:name => 'mmm'}
314
318
  end
315
319
 
320
+ specify "should keep a composite primary key when dropping columns" do
321
+ @db.create_table!(:test2){Integer :a; Integer :b; Integer :c; primary_key [:a, :b]}
322
+ @db.drop_column :test2, :c
323
+ @db[:test2].columns.should == [:a, :b]
324
+ @db[:test2] << {:a=>1, :b=>2}
325
+ @db[:test2] << {:a=>2, :b=>3}
326
+ proc{@db[:test2] << {:a=>2, :b=>3}}.should raise_error(Sequel::Error)
327
+ end
328
+
316
329
  specify "should keep column attributes when dropping a column" do
317
330
  @db.create_table! :test3 do
318
331
  primary_key :id
@@ -412,14 +425,15 @@ describe "A SQLite database" do
412
425
  specify "should choose a temporary table name that isn't already used when dropping or renaming columns" do
413
426
  sqls = []
414
427
  @db.loggers << (l=Class.new{%w'info error'.each{|m| define_method(m){|sql| sqls << sql}}}.new)
415
- @db.create_table! :test3 do
428
+ @db.tables.each{|t| @db.drop_table(t) if t.to_s =~ /test3/}
429
+ @db.create_table :test3 do
416
430
  Integer :h
417
431
  Integer :i
418
432
  end
419
- @db.create_table! :test3_backup0 do
433
+ @db.create_table :test3_backup0 do
420
434
  Integer :j
421
435
  end
422
- @db.create_table! :test3_backup1 do
436
+ @db.create_table :test3_backup1 do
423
437
  Integer :k
424
438
  end
425
439
 
@@ -428,20 +442,20 @@ describe "A SQLite database" do
428
442
  @db[:test3_backup1].columns.should == [:k]
429
443
  sqls.clear
430
444
  @db.drop_column(:test3, :i)
431
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup2/}.should == true
432
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup[01]/}.should == false
445
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup2/}.should == true
446
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup[01]/}.should == false
433
447
  @db[:test3].columns.should == [:h]
434
448
  @db[:test3_backup0].columns.should == [:j]
435
449
  @db[:test3_backup1].columns.should == [:k]
436
450
 
437
- @db.create_table! :test3_backup2 do
451
+ @db.create_table :test3_backup2 do
438
452
  Integer :l
439
453
  end
440
454
 
441
455
  sqls.clear
442
456
  @db.rename_column(:test3, :h, :i)
443
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup3/}.should == true
444
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup[012]/}.should == false
457
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup3/}.should == true
458
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup[012]/}.should == false
445
459
  @db[:test3].columns.should == [:i]
446
460
  @db[:test3_backup0].columns.should == [:j]
447
461
  @db[:test3_backup1].columns.should == [:k]