sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -26,9 +26,7 @@ def logger.method_missing(m, msg)
26
26
  MYSQL_DB.sqls << msg
27
27
  end
28
28
  MYSQL_DB.loggers = [logger]
29
- MYSQL_DB.drop_table(:items) rescue nil
30
- MYSQL_DB.drop_table(:dolls) rescue nil
31
- MYSQL_DB.drop_table(:booltest) rescue nil
29
+ MYSQL_DB.drop_table?(:items, :dolls, :booltest)
32
30
 
33
31
  SQL_BEGIN = 'BEGIN'
34
32
  SQL_ROLLBACK = 'ROLLBACK'
@@ -40,29 +38,29 @@ describe "MySQL", '#create_table' do
40
38
  MYSQL_DB.sqls.clear
41
39
  end
42
40
  after do
43
- @db.drop_table(:dolls) rescue nil
41
+ @db.drop_table?(:dolls)
44
42
  end
45
-
43
+
46
44
  specify "should allow to specify options for MySQL" do
47
45
  @db.create_table(:dolls, :engine => 'MyISAM', :charset => 'latin2'){text :name}
48
- @db.sqls.should == ["CREATE TABLE dolls (name text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
46
+ @db.sqls.should == ["CREATE TABLE `dolls` (`name` text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
49
47
  end
50
-
48
+
51
49
  specify "should create a temporary table" do
52
50
  @db.create_table(:tmp_dolls, :temp => true, :engine => 'MyISAM', :charset => 'latin2'){text :name}
53
- @db.sqls.should == ["CREATE TEMPORARY TABLE tmp_dolls (name text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
51
+ @db.sqls.should == ["CREATE TEMPORARY TABLE `tmp_dolls` (`name` text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
54
52
  end
55
-
53
+
56
54
  specify "should not use a default for a String :text=>true type" do
57
55
  @db.create_table(:dolls){String :name, :text=>true, :default=>'blah'}
58
- @db.sqls.should == ["CREATE TABLE dolls (name text)"]
56
+ @db.sqls.should == ["CREATE TABLE `dolls` (`name` text)"]
59
57
  end
60
-
58
+
61
59
  specify "should not use a default for a File type" do
62
60
  @db.create_table(:dolls){File :name, :default=>'blah'}
63
- @db.sqls.should == ["CREATE TABLE dolls (name blob)"]
61
+ @db.sqls.should == ["CREATE TABLE `dolls` (`name` blob)"]
64
62
  end
65
-
63
+
66
64
  specify "should respect the size option for File type" do
67
65
  @db.create_table(:dolls) do
68
66
  File :n1
@@ -82,17 +80,23 @@ describe "MySQL", '#create_table' do
82
80
  end
83
81
  @db.schema(:dolls).map{|k, v| v[:auto_increment]}.should == [nil, nil, true]
84
82
  end
83
+
84
+ cspecify "should support collate with various other column options", :swift do
85
+ @db.create_table!(:dolls){ String :name, :size=>128, :collate=>:utf8_bin, :default=>'foo', :null=>false, :unique=>true}
86
+ @db[:dolls].insert
87
+ @db[:dolls].select_map(:name).should == ["foo"]
88
+ end
85
89
  end
86
90
 
87
91
  describe "A MySQL database" do
88
92
  specify "should provide the server version" do
89
93
  MYSQL_DB.server_version.should >= 40000
90
94
  end
91
-
95
+
92
96
  specify "should handle the creation and dropping of an InnoDB table with foreign keys" do
93
97
  proc{MYSQL_DB.create_table!(:test_innodb, :engine=>:InnoDB){primary_key :id; foreign_key :fk, :test_innodb, :key=>:id}}.should_not raise_error
94
98
  end
95
-
99
+
96
100
  specify "should support for_share" do
97
101
  MYSQL_DB.transaction{MYSQL_DB[:test2].for_share.all.should == []}
98
102
  end
@@ -106,18 +110,18 @@ if MYSQL_DB.adapter_scheme == :mysql
106
110
  @ds = @db[:booltest]
107
111
  end
108
112
  after do
109
- Sequel::MySQL.convert_tinyint_to_bool = true
110
- @db.drop_table(:booltest)
113
+ @db.convert_tinyint_to_bool = true
114
+ @db.drop_table?(:booltest)
111
115
  end
112
-
116
+
113
117
  specify "should consider tinyint(1) datatypes as boolean if set, but not larger tinyints" do
114
118
  @db.schema(:booltest, :reload=>true).should == [[:b, {:type=>:boolean, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(1)"}, ], [:i, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(4)"}, ]]
115
- Sequel::MySQL.convert_tinyint_to_bool = false
119
+ @db.convert_tinyint_to_bool = false
116
120
  @db.schema(:booltest, :reload=>true).should == [[:b, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(1)"}, ], [:i, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(4)"}, ]]
117
121
  end
118
-
122
+
119
123
  specify "should return tinyint(1)s as bools and tinyint(4)s as integers when set" do
120
- Sequel::MySQL.convert_tinyint_to_bool = true
124
+ @db.convert_tinyint_to_bool = true
121
125
  @ds.delete
122
126
  @ds << {:b=>true, :i=>10}
123
127
  @ds.all.should == [{:b=>true, :i=>10}]
@@ -130,14 +134,14 @@ if MYSQL_DB.adapter_scheme == :mysql
130
134
  end
131
135
 
132
136
  specify "should return all tinyints as integers when unset" do
133
- Sequel::MySQL.convert_tinyint_to_bool = false
137
+ @db.convert_tinyint_to_bool = false
134
138
  @ds.delete
135
139
  @ds << {:b=>true, :i=>10}
136
140
  @ds.all.should == [{:b=>1, :i=>10}]
137
141
  @ds.delete
138
142
  @ds << {:b=>false, :i=>0}
139
143
  @ds.all.should == [{:b=>0, :i=>0}]
140
-
144
+
141
145
  @ds.delete
142
146
  @ds << {:b=>1, :i=>10}
143
147
  @ds.all.should == [{:b=>1, :i=>10}]
@@ -155,20 +159,20 @@ describe "A MySQL dataset" do
155
159
  MYSQL_DB.sqls.clear
156
160
  end
157
161
  after do
158
- MYSQL_DB.drop_table(:items)
162
+ MYSQL_DB.drop_table?(:items)
159
163
  end
160
-
164
+
161
165
  specify "should quote columns and tables using back-ticks if quoting identifiers" do
162
166
  @d.quote_identifiers = true
163
167
  @d.select(:name).sql.should == \
164
168
  'SELECT `name` FROM `items`'
165
-
169
+
166
170
  @d.select('COUNT(*)'.lit).sql.should == \
167
171
  'SELECT COUNT(*) FROM `items`'
168
172
 
169
173
  @d.select(:max.sql_function(:value)).sql.should == \
170
174
  'SELECT max(`value`) FROM `items`'
171
-
175
+
172
176
  @d.select(:NOW.sql_function).sql.should == \
173
177
  'SELECT NOW() FROM `items`'
174
178
 
@@ -180,13 +184,13 @@ describe "A MySQL dataset" do
180
184
 
181
185
  @d.select('items.name AS item_name'.lit).sql.should == \
182
186
  'SELECT items.name AS item_name FROM `items`'
183
-
187
+
184
188
  @d.select('`name`'.lit).sql.should == \
185
189
  'SELECT `name` FROM `items`'
186
190
 
187
191
  @d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
188
192
  'SELECT max(items.`name`) AS `max_name` FROM `items`'
189
-
193
+
190
194
  @d.select(:test.sql_function(:abc, 'hello')).sql.should == \
191
195
  "SELECT test(`abc`, 'hello') FROM `items`"
192
196
 
@@ -202,7 +206,7 @@ describe "A MySQL dataset" do
202
206
  @d.insert_sql(:x => :y).should == \
203
207
  'INSERT INTO `items` (`x`) VALUES (`y`)'
204
208
  end
205
-
209
+
206
210
  specify "should quote fields correctly when reversing the order" do
207
211
  @d.quote_identifiers = true
208
212
  @d.reverse_order(:name).sql.should == \
@@ -217,37 +221,53 @@ describe "A MySQL dataset" do
217
221
  @d.reverse_order(:name.desc, :test).sql.should == \
218
222
  'SELECT * FROM `items` ORDER BY `name` ASC, `test` DESC'
219
223
  end
220
-
224
+
221
225
  specify "should support ORDER clause in UPDATE statements" do
222
226
  @d.order(:name).update_sql(:value => 1).should == \
223
- 'UPDATE items SET value = 1 ORDER BY name'
227
+ 'UPDATE `items` SET `value` = 1 ORDER BY `name`'
224
228
  end
225
-
229
+
226
230
  specify "should support LIMIT clause in UPDATE statements" do
227
231
  @d.limit(10).update_sql(:value => 1).should == \
228
- 'UPDATE items SET value = 1 LIMIT 10'
232
+ 'UPDATE `items` SET `value` = 1 LIMIT 10'
229
233
  end
230
-
234
+
231
235
  specify "should support regexps" do
232
236
  @d << {:name => 'abc', :value => 1}
233
237
  @d << {:name => 'bcd', :value => 2}
234
238
  @d.filter(:name => /bc/).count.should == 2
235
239
  @d.filter(:name => /^bc/).count.should == 1
236
240
  end
237
-
241
+
242
+ specify "should have explain output" do
243
+ @d.explain.should be_a_kind_of(String)
244
+ @d.explain(:extended=>true).should be_a_kind_of(String)
245
+ @d.explain.should_not == @d.explain(:extended=>true)
246
+ end
247
+
238
248
  specify "should correctly literalize strings with comment backslashes in them" do
239
249
  @d.delete
240
250
  proc {@d << {:name => ':\\'}}.should_not raise_error
241
-
251
+
242
252
  @d.first[:name].should == ':\\'
243
253
  end
254
+
255
+ specify "should handle prepared statements with on_duplicate_key_update" do
256
+ @d.db.add_index :items, :value, :unique=>true
257
+ ds = @d.on_duplicate_key_update
258
+ ps = ds.prepare(:insert, :insert_user_id_feature_name, :value => :$v, :name => :$n)
259
+ ps.call(:v => 1, :n => 'a')
260
+ ds.all.should == [{:value=>1, :name=>'a'}]
261
+ ps.call(:v => 1, :n => 'b')
262
+ ds.all.should == [{:value=>1, :name=>'b'}]
263
+ end
244
264
  end
245
265
 
246
266
  describe "MySQL datasets" do
247
267
  before do
248
268
  @d = MYSQL_DB[:orders]
249
269
  end
250
-
270
+
251
271
  specify "should correctly quote column references" do
252
272
  @d.quote_identifiers = true
253
273
  market = 'ICE'
@@ -269,9 +289,9 @@ describe "Dataset#distinct" do
269
289
  @ds = @db[:a]
270
290
  end
271
291
  after do
272
- @db.drop_table(:a)
292
+ @db.drop_table?(:a)
273
293
  end
274
-
294
+
275
295
  it "#distinct with arguments should return results distinct on those arguments" do
276
296
  @ds.insert(20, 10)
277
297
  @ds.insert(30, 10)
@@ -293,43 +313,43 @@ describe "MySQL join expressions" do
293
313
  end
294
314
  specify "should support natural left joins" do
295
315
  @ds.join_table(:natural_left, :nodes).sql.should == \
296
- 'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
316
+ 'SELECT * FROM `nodes` NATURAL LEFT JOIN `nodes`'
297
317
  end
298
318
  specify "should support natural right joins" do
299
319
  @ds.join_table(:natural_right, :nodes).sql.should == \
300
- 'SELECT * FROM nodes NATURAL RIGHT JOIN nodes'
320
+ 'SELECT * FROM `nodes` NATURAL RIGHT JOIN `nodes`'
301
321
  end
302
322
  specify "should support natural left outer joins" do
303
323
  @ds.join_table(:natural_left_outer, :nodes).sql.should == \
304
- 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN nodes'
324
+ 'SELECT * FROM `nodes` NATURAL LEFT OUTER JOIN `nodes`'
305
325
  end
306
326
  specify "should support natural right outer joins" do
307
327
  @ds.join_table(:natural_right_outer, :nodes).sql.should == \
308
- 'SELECT * FROM nodes NATURAL RIGHT OUTER JOIN nodes'
328
+ 'SELECT * FROM `nodes` NATURAL RIGHT OUTER JOIN `nodes`'
309
329
  end
310
330
  specify "should support natural inner joins" do
311
331
  @ds.join_table(:natural_inner, :nodes).sql.should == \
312
- 'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
332
+ 'SELECT * FROM `nodes` NATURAL LEFT JOIN `nodes`'
313
333
  end
314
334
  specify "should support cross joins" do
315
335
  @ds.join_table(:cross, :nodes).sql.should == \
316
- 'SELECT * FROM nodes CROSS JOIN nodes'
336
+ 'SELECT * FROM `nodes` CROSS JOIN `nodes`'
317
337
  end
318
338
  specify "should support cross joins as inner joins if conditions are used" do
319
339
  @ds.join_table(:cross, :nodes, :id=>:id).sql.should == \
320
- 'SELECT * FROM nodes INNER JOIN nodes ON (nodes.id = nodes.id)'
340
+ 'SELECT * FROM `nodes` INNER JOIN `nodes` ON (`nodes`.`id` = `nodes`.`id`)'
321
341
  end
322
342
  specify "should support straight joins (force left table to be read before right)" do
323
343
  @ds.join_table(:straight, :nodes).sql.should == \
324
- 'SELECT * FROM nodes STRAIGHT_JOIN nodes'
344
+ 'SELECT * FROM `nodes` STRAIGHT_JOIN `nodes`'
325
345
  end
326
346
  specify "should support natural joins on multiple tables." do
327
347
  @ds.join_table(:natural_left_outer, [:nodes, :branches]).sql.should == \
328
- 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN (nodes, branches)'
348
+ 'SELECT * FROM `nodes` NATURAL LEFT OUTER JOIN (`nodes`, `branches`)'
329
349
  end
330
350
  specify "should support straight joins on multiple tables." do
331
351
  @ds.join_table(:straight, [:nodes,:branches]).sql.should == \
332
- 'SELECT * FROM nodes STRAIGHT_JOIN (nodes, branches)'
352
+ 'SELECT * FROM `nodes` STRAIGHT_JOIN (`nodes`, `branches`)'
333
353
  end
334
354
  end
335
355
 
@@ -337,23 +357,23 @@ describe "Joined MySQL dataset" do
337
357
  before do
338
358
  @ds = MYSQL_DB[:nodes]
339
359
  end
340
-
360
+
341
361
  specify "should quote fields correctly" do
342
362
  @ds.quote_identifiers = true
343
363
  @ds.join(:attributes, :node_id => :id).sql.should == \
344
364
  "SELECT * FROM `nodes` INNER JOIN `attributes` ON (`attributes`.`node_id` = `nodes`.`id`)"
345
365
  end
346
-
366
+
347
367
  specify "should allow a having clause on ungrouped datasets" do
348
368
  proc {@ds.having('blah')}.should_not raise_error
349
369
 
350
370
  @ds.having('blah').sql.should == \
351
- "SELECT * FROM nodes HAVING (blah)"
371
+ "SELECT * FROM `nodes` HAVING (blah)"
352
372
  end
353
-
373
+
354
374
  specify "should put a having clause before an order by clause" do
355
375
  @ds.order(:aaa).having(:bbb => :ccc).sql.should == \
356
- "SELECT * FROM nodes HAVING (bbb = ccc) ORDER BY aaa"
376
+ "SELECT * FROM `nodes` HAVING (`bbb` = `ccc`) ORDER BY `aaa`"
357
377
  end
358
378
  end
359
379
 
@@ -364,19 +384,19 @@ describe "A MySQL database" do
364
384
 
365
385
  specify "should support add_column operations" do
366
386
  @db.add_column :test2, :xyz, :text
367
-
387
+
368
388
  @db[:test2].columns.should == [:name, :value, :xyz]
369
389
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => '000'}
370
390
  @db[:test2].first[:xyz].should == '000'
371
391
  end
372
-
392
+
373
393
  specify "should support drop_column operations" do
374
394
  @db[:test2].columns.should == [:name, :value, :xyz]
375
395
  @db.drop_column :test2, :xyz
376
-
396
+
377
397
  @db[:test2].columns.should == [:name, :value]
378
398
  end
379
-
399
+
380
400
  specify "should support rename_column operations" do
381
401
  @db[:test2].delete
382
402
  @db.add_column :test2, :xyz, :text
@@ -387,7 +407,7 @@ describe "A MySQL database" do
387
407
  @db[:test2].columns.should == [:name, :value, :zyx]
388
408
  @db[:test2].first[:zyx].should == 'qqqq'
389
409
  end
390
-
410
+
391
411
  specify "should support rename_column operations with types like varchar(255)" do
392
412
  @db[:test2].delete
393
413
  @db.add_column :test2, :tre, :text
@@ -398,26 +418,27 @@ describe "A MySQL database" do
398
418
  @db[:test2].columns.should == [:name, :value, :zyx, :ert]
399
419
  @db[:test2].first[:ert].should == 'qqqq'
400
420
  end
401
-
421
+
402
422
  specify "should support set_column_type operations" do
403
423
  @db.add_column :test2, :xyz, :float
404
424
  @db[:test2].delete
405
425
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
406
426
  @db.set_column_type :test2, :xyz, :integer
407
-
427
+
408
428
  @db[:test2].first[:xyz].should == 57
409
429
  end
410
-
430
+
411
431
  specify "should support add_index" do
412
432
  @db.add_index :test2, :value
413
433
  end
414
-
434
+
415
435
  specify "should support drop_index" do
416
436
  @db.drop_index :test2, :value
417
437
  end
418
-
438
+
419
439
  specify "should support add_foreign_key" do
420
440
  @db.alter_table :test2 do
441
+ add_index :value, :unique=>true
421
442
  add_foreign_key :value2, :test2, :key=>:value
422
443
  end
423
444
  @db[:test2].columns.should == [:name, :value, :zyx, :ert, :xyz, :value2]
@@ -427,79 +448,83 @@ end
427
448
  describe "A MySQL database with table options" do
428
449
  before do
429
450
  @options = {:engine=>'MyISAM', :charset=>'latin1', :collate => 'latin1_swedish_ci'}
430
-
451
+
431
452
  Sequel::MySQL.default_engine = 'InnoDB'
432
453
  Sequel::MySQL.default_charset = 'utf8'
433
454
  Sequel::MySQL.default_collate = 'utf8_general_ci'
434
-
455
+
435
456
  @db = MYSQL_DB
436
- @db.drop_table(:items) rescue nil
437
-
457
+ @db.drop_table?(:items)
458
+
438
459
  MYSQL_DB.sqls.clear
439
460
  end
440
461
  after do
441
- @db.drop_table(:items) rescue nil
462
+ @db.drop_table?(:items)
442
463
 
443
464
  Sequel::MySQL.default_engine = nil
444
465
  Sequel::MySQL.default_charset = nil
445
466
  Sequel::MySQL.default_collate = nil
446
467
  end
447
-
468
+
448
469
  specify "should allow to pass custom options (engine, charset, collate) for table creation" do
449
470
  @db.create_table(:items, @options){Integer :size; text :name}
450
- @db.sqls.should == ["CREATE TABLE items (size integer, name text) ENGINE=MyISAM DEFAULT CHARSET=latin1 DEFAULT COLLATE=latin1_swedish_ci"]
471
+ @db.sqls.should == ["CREATE TABLE `items` (`size` integer, `name` text) ENGINE=MyISAM DEFAULT CHARSET=latin1 DEFAULT COLLATE=latin1_swedish_ci"]
451
472
  end
452
-
473
+
453
474
  specify "should use default options if specified (engine, charset, collate) for table creation" do
454
475
  @db.create_table(:items){Integer :size; text :name}
455
- @db.sqls.should == ["CREATE TABLE items (size integer, name text) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci"]
476
+ @db.sqls.should == ["CREATE TABLE `items` (`size` integer, `name` text) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci"]
456
477
  end
457
-
478
+
458
479
  specify "should not use default if option has a nil value" do
459
480
  @db.create_table(:items, :engine=>nil, :charset=>nil, :collate=>nil){Integer :size; text :name}
460
- @db.sqls.should == ["CREATE TABLE items (size integer, name text)"]
481
+ @db.sqls.should == ["CREATE TABLE `items` (`size` integer, `name` text)"]
461
482
  end
462
483
  end
463
484
 
464
485
  describe "A MySQL database" do
465
486
  before do
466
487
  @db = MYSQL_DB
467
- @db.drop_table(:items) rescue nil
488
+ @db.drop_table?(:items)
468
489
  MYSQL_DB.sqls.clear
469
490
  end
470
491
  after do
471
- @db.drop_table(:items) rescue nil
492
+ @db.drop_table?(:items, :users)
472
493
  end
473
-
494
+
474
495
  specify "should support defaults for boolean columns" do
475
496
  @db.create_table(:items){TrueClass :active1, :default=>true; FalseClass :active2, :default => false}
476
- @db.sqls.should == ["CREATE TABLE items (active1 tinyint(1) DEFAULT 1, active2 tinyint(1) DEFAULT 0)"]
497
+ @db.sqls.should == ["CREATE TABLE `items` (`active1` tinyint(1) DEFAULT 1, `active2` tinyint(1) DEFAULT 0)"]
477
498
  end
478
-
499
+
479
500
  specify "should correctly format CREATE TABLE statements with foreign keys" do
480
- @db.create_table(:items){Integer :id; foreign_key :p_id, :items, :key => :id, :null => false, :on_delete => :cascade}
481
- @db.sqls.should == ["CREATE TABLE items (id integer, p_id integer NOT NULL, FOREIGN KEY (p_id) REFERENCES items(id) ON DELETE CASCADE)"]
501
+ @db.create_table(:items){primary_key :id; foreign_key :p_id, :items, :key => :id, :null => false, :on_delete => :cascade}
502
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer PRIMARY KEY AUTO_INCREMENT, `p_id` integer NOT NULL, UNIQUE (`id`), FOREIGN KEY (`p_id`) REFERENCES `items`(`id`) ON DELETE CASCADE)"]
482
503
  end
483
-
504
+
484
505
  specify "should correctly format ALTER TABLE statements with foreign keys" do
485
506
  @db.create_table(:items){Integer :id}
507
+ @db.create_table(:users){primary_key :id}
486
508
  @db.alter_table(:items){add_foreign_key :p_id, :users, :key => :id, :null => false, :on_delete => :cascade}
487
- @db.sqls.should == ["CREATE TABLE items (id integer)", "ALTER TABLE items ADD COLUMN p_id integer NOT NULL", "ALTER TABLE items ADD FOREIGN KEY (p_id) REFERENCES users(id) ON DELETE CASCADE"]
509
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer)",
510
+ "CREATE TABLE `users` (`id` integer PRIMARY KEY AUTO_INCREMENT)",
511
+ "ALTER TABLE `items` ADD COLUMN `p_id` integer NOT NULL",
512
+ "ALTER TABLE `items` ADD FOREIGN KEY (`p_id`) REFERENCES `users`(`id`) ON DELETE CASCADE"]
488
513
  end
489
-
514
+
490
515
  specify "should have rename_column support keep existing options" do
491
516
  @db.create_table(:items){String :id, :null=>false, :default=>'blah'}
492
517
  @db.alter_table(:items){rename_column :id, :nid}
493
- @db.sqls.should == ["CREATE TABLE items (id varchar(255) NOT NULL DEFAULT 'blah')", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id nid varchar(255) NOT NULL DEFAULT 'blah'"]
518
+ @db.sqls.should == ["CREATE TABLE `items` (`id` varchar(255) NOT NULL DEFAULT 'blah')", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `nid` varchar(255) NOT NULL DEFAULT 'blah'"]
494
519
  @db[:items].insert
495
520
  @db[:items].all.should == [{:nid=>'blah'}]
496
521
  proc{@db[:items].insert(:nid=>nil)}.should raise_error(Sequel::DatabaseError)
497
522
  end
498
-
523
+
499
524
  specify "should have set_column_type support keep existing options" do
500
525
  @db.create_table(:items){Integer :id, :null=>false, :default=>5}
501
526
  @db.alter_table(:items){set_column_type :id, Bignum}
502
- @db.sqls.should == ["CREATE TABLE items (id integer NOT NULL DEFAULT 5)", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id bigint NOT NULL DEFAULT 5"]
527
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer NOT NULL DEFAULT 5)", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` bigint NOT NULL DEFAULT 5"]
503
528
  @db[:items].insert
504
529
  @db[:items].all.should == [{:id=>5}]
505
530
  proc{@db[:items].insert(:id=>nil)}.should raise_error(Sequel::DatabaseError)
@@ -511,35 +536,35 @@ describe "A MySQL database" do
511
536
  specify "should have set_column_type pass through options" do
512
537
  @db.create_table(:items){integer :id; enum :list, :elements=>%w[one]}
513
538
  @db.alter_table(:items){set_column_type :id, :int, :unsigned=>true, :size=>8; set_column_type :list, :enum, :elements=>%w[two]}
514
- @db.sqls.should == ["CREATE TABLE items (id integer, list enum('one'))", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id int(8) UNSIGNED NULL", "ALTER TABLE items CHANGE COLUMN list list enum('two') NULL"]
539
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer, `list` enum('one'))", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` int(8) UNSIGNED NULL", "ALTER TABLE `items` CHANGE COLUMN `list` `list` enum('two') NULL"]
515
540
  end
516
-
541
+
517
542
  specify "should have set_column_default support keep existing options" do
518
543
  @db.create_table(:items){Integer :id, :null=>false, :default=>5}
519
544
  @db.alter_table(:items){set_column_default :id, 6}
520
- @db.sqls.should == ["CREATE TABLE items (id integer NOT NULL DEFAULT 5)", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id int(11) NOT NULL DEFAULT 6"]
545
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer NOT NULL DEFAULT 5)", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` int(11) NOT NULL DEFAULT 6"]
521
546
  @db[:items].insert
522
547
  @db[:items].all.should == [{:id=>6}]
523
548
  proc{@db[:items].insert(:id=>nil)}.should raise_error(Sequel::DatabaseError)
524
549
  end
525
-
550
+
526
551
  specify "should have set_column_allow_null support keep existing options" do
527
552
  @db.create_table(:items){Integer :id, :null=>false, :default=>5}
528
553
  @db.alter_table(:items){set_column_allow_null :id, true}
529
- @db.sqls.should == ["CREATE TABLE items (id integer NOT NULL DEFAULT 5)", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id id int(11) NULL DEFAULT 5"]
554
+ @db.sqls.should == ["CREATE TABLE `items` (`id` integer NOT NULL DEFAULT 5)", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` int(11) NULL DEFAULT 5"]
530
555
  @db[:items].insert
531
556
  @db[:items].all.should == [{:id=>5}]
532
557
  proc{@db[:items].insert(:id=>nil)}.should_not
533
558
  end
534
-
559
+
535
560
  specify "should accept repeated raw sql statements using Database#<<" do
536
561
  @db.create_table(:items){String :name; Integer :value}
537
562
  @db << 'DELETE FROM items'
538
563
  @db[:items].count.should == 0
539
-
564
+
540
565
  @db << "INSERT INTO items (name, value) VALUES ('tutu', 1234)"
541
566
  @db[:items].first.should == {:name => 'tutu', :value => 1234}
542
-
567
+
543
568
  @db << 'DELETE FROM items'
544
569
  @db[:items].first.should == nil
545
570
  end
@@ -552,12 +577,12 @@ if %w'localhost 127.0.0.1 ::1'.include?(MYSQL_URI.host) and MYSQL_DB.adapter_sch
552
577
  db = Sequel.mysql(MYSQL_DB.opts[:database], :host => 'localhost', :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
553
578
  proc {db.test_connection}.should_not raise_error
554
579
  end
555
-
580
+
556
581
  specify "should accept a socket option without host option" do
557
582
  db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
558
583
  proc {db.test_connection}.should_not raise_error
559
584
  end
560
-
585
+
561
586
  specify "should fail to connect with invalid socket" do
562
587
  db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket =>'blah')
563
588
  proc {db.test_connection}.should raise_error
@@ -577,9 +602,58 @@ describe "A MySQL database" do
577
602
  end
578
603
  end
579
604
 
605
+ describe "MySQL foreign key support" do
606
+ after do
607
+ MYSQL_DB.drop_table?(:testfk, :testpk)
608
+ end
609
+
610
+ specify "should create table without :key" do
611
+ MYSQL_DB.create_table!(:testpk){primary_key :id}
612
+ MYSQL_DB.create_table!(:testfk){foreign_key :fk, :testpk}
613
+ end
614
+
615
+ specify "should create table with composite keys without :key" do
616
+ MYSQL_DB.create_table!(:testpk){Integer :id; Integer :id2; primary_key([:id, :id2])}
617
+ MYSQL_DB.create_table!(:testfk){Integer :fk; Integer :fk2; foreign_key([:fk, :fk2], :testpk)}
618
+ end
619
+
620
+ specify "should create table with self referential without :key" do
621
+ MYSQL_DB.create_table!(:testfk){primary_key :id; foreign_key :fk, :testfk}
622
+ end
623
+
624
+ specify "should create table with self referential with composite keys without :key" do
625
+ MYSQL_DB.create_table!(:testfk){Integer :id; Integer :id2; Integer :fk; Integer :fk2; primary_key([:id, :id2]); foreign_key([:fk, :fk2], :testfk)}
626
+ end
627
+
628
+ specify "should alter table without :key" do
629
+ MYSQL_DB.create_table!(:testpk){primary_key :id}
630
+ MYSQL_DB.create_table!(:testfk){Integer :id}
631
+ MYSQL_DB.alter_table(:testfk){add_foreign_key :fk, :testpk}
632
+ end
633
+
634
+ specify "should alter table with composite keys without :key" do
635
+ MYSQL_DB.create_table!(:testpk){Integer :id; Integer :id2; primary_key([:id, :id2])}
636
+ MYSQL_DB.create_table!(:testfk){Integer :fk; Integer :fk2}
637
+ MYSQL_DB.alter_table(:testfk){add_foreign_key([:fk, :fk2], :testpk)}
638
+ end
639
+
640
+ specify "should alter table with self referential without :key" do
641
+ MYSQL_DB.create_table!(:testfk){primary_key :id}
642
+ MYSQL_DB.alter_table(:testfk){add_foreign_key :fk, :testfk}
643
+ end
644
+
645
+ specify "should alter table with self referential with composite keys without :key" do
646
+ MYSQL_DB.create_table!(:testfk){Integer :id; Integer :id2; Integer :fk; Integer :fk2; primary_key([:id, :id2])}
647
+ MYSQL_DB.alter_table(:testfk){add_foreign_key [:fk, :fk2], :testfk}
648
+ end
649
+ end
650
+
580
651
  describe "A grouped MySQL dataset" do
581
652
  before do
582
- MYSQL_DB[:test2].delete
653
+ MYSQL_DB.create_table! :test2 do
654
+ text :name
655
+ integer :value
656
+ end
583
657
  MYSQL_DB[:test2] << {:name => '11', :value => 10}
584
658
  MYSQL_DB[:test2] << {:name => '11', :value => 20}
585
659
  MYSQL_DB[:test2] << {:name => '11', :value => 30}
@@ -587,12 +661,12 @@ describe "A grouped MySQL dataset" do
587
661
  MYSQL_DB[:test2] << {:name => '12', :value => 20}
588
662
  MYSQL_DB[:test2] << {:name => '13', :value => 10}
589
663
  end
590
-
664
+
591
665
  specify "should return the correct count for raw sql query" do
592
666
  ds = MYSQL_DB["select name FROM test2 WHERE name = '11' GROUP BY name"]
593
667
  ds.count.should == 1
594
668
  end
595
-
669
+
596
670
  specify "should return the correct count for a normal dataset" do
597
671
  ds = MYSQL_DB[:test2].select(:name).where(:name => '11').group(:name)
598
672
  ds.count.should == 1
@@ -602,21 +676,21 @@ end
602
676
  describe "A MySQL database" do
603
677
  before do
604
678
  @db = MYSQL_DB
605
- @db.drop_table(:posts) rescue nil
679
+ @db.drop_table?(:posts)
606
680
  @db.sqls.clear
607
681
  end
608
682
  after do
609
- @db.drop_table(:posts) rescue nil
683
+ @db.drop_table?(:posts)
610
684
  end
611
-
685
+
612
686
  specify "should support fulltext indexes and full_text_search" do
613
- @db.create_table(:posts){text :title; text :body; full_text_index :title; full_text_index [:title, :body]}
687
+ @db.create_table(:posts, :engine=>:MyISAM){text :title; text :body; full_text_index :title; full_text_index [:title, :body]}
614
688
  @db.sqls.should == [
615
- "CREATE TABLE posts (title text, body text)",
616
- "CREATE FULLTEXT INDEX posts_title_index ON posts (title)",
617
- "CREATE FULLTEXT INDEX posts_title_body_index ON posts (title, body)"
689
+ "CREATE TABLE `posts` (`title` text, `body` text) ENGINE=MyISAM",
690
+ "CREATE FULLTEXT INDEX `posts_title_index` ON `posts` (`title`)",
691
+ "CREATE FULLTEXT INDEX `posts_title_body_index` ON `posts` (`title`, `body`)"
618
692
  ]
619
-
693
+
620
694
  @db[:posts].insert(:title=>'ruby rails', :body=>'y')
621
695
  @db[:posts].insert(:title=>'sequel', :body=>'ruby')
622
696
  @db[:posts].insert(:title=>'ruby scooby', :body=>'x')
@@ -626,32 +700,35 @@ describe "A MySQL database" do
626
700
  @db[:posts].full_text_search([:title, :body], ['sequel', 'ruby']).all.should == [{:title=>'sequel', :body=>'ruby'}]
627
701
  @db[:posts].full_text_search(:title, '+ruby -rails', :boolean => true).all.should == [{:title=>'ruby scooby', :body=>'x'}]
628
702
  @db.sqls.should == [
629
- "SELECT * FROM posts WHERE (MATCH (title) AGAINST ('rails'))",
630
- "SELECT * FROM posts WHERE (MATCH (title, body) AGAINST ('sequel ruby'))",
631
- "SELECT * FROM posts WHERE (MATCH (title) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"]
703
+ "SELECT * FROM `posts` WHERE (MATCH (`title`) AGAINST ('rails'))",
704
+ "SELECT * FROM `posts` WHERE (MATCH (`title`, `body`) AGAINST ('sequel ruby'))",
705
+ "SELECT * FROM `posts` WHERE (MATCH (`title`) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"]
706
+
707
+ @db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
708
+ @db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'y'}]
632
709
  end
633
710
 
634
711
  specify "should support spatial indexes" do
635
- @db.create_table(:posts){point :geom, :null=>false; spatial_index [:geom]}
712
+ @db.create_table(:posts, :engine=>:MyISAM){point :geom, :null=>false; spatial_index [:geom]}
636
713
  @db.sqls.should == [
637
- "CREATE TABLE posts (geom point NOT NULL)",
638
- "CREATE SPATIAL INDEX posts_geom_index ON posts (geom)"
714
+ "CREATE TABLE `posts` (`geom` point NOT NULL) ENGINE=MyISAM",
715
+ "CREATE SPATIAL INDEX `posts_geom_index` ON `posts` (`geom`)"
639
716
  ]
640
717
  end
641
718
 
642
719
  specify "should support indexes with index type" do
643
720
  @db.create_table(:posts){Integer :id; index :id, :type => :btree}
644
721
  @db.sqls.should == [
645
- "CREATE TABLE posts (id integer)",
646
- "CREATE INDEX posts_id_index USING btree ON posts (id)"
722
+ "CREATE TABLE `posts` (`id` integer)",
723
+ "CREATE INDEX `posts_id_index` USING btree ON `posts` (`id`)"
647
724
  ]
648
725
  end
649
726
 
650
727
  specify "should support unique indexes with index type" do
651
728
  @db.create_table(:posts){Integer :id; index :id, :type => :btree, :unique => true}
652
729
  @db.sqls.should == [
653
- "CREATE TABLE posts (id integer)",
654
- "CREATE UNIQUE INDEX posts_id_index USING btree ON posts (id)"
730
+ "CREATE TABLE `posts` (`id` integer)",
731
+ "CREATE UNIQUE INDEX `posts_id_index` USING btree ON `posts` (`id`)"
655
732
  ]
656
733
  end
657
734
 
@@ -675,43 +752,25 @@ describe "MySQL::Dataset#insert and related methods" do
675
752
  MYSQL_DB.sqls.clear
676
753
  end
677
754
  after do
678
- MYSQL_DB.drop_table(:items)
755
+ MYSQL_DB.drop_table?(:items)
679
756
  end
680
757
 
681
758
  specify "#insert should insert record with default values when no arguments given" do
682
759
  @d.insert
683
-
684
- MYSQL_DB.sqls.should == [
685
- "INSERT INTO items () VALUES ()"
686
- ]
687
-
688
- @d.all.should == [
689
- {:name => nil, :value => nil}
690
- ]
760
+ MYSQL_DB.sqls.should == ["INSERT INTO `items` () VALUES ()"]
761
+ @d.all.should == [{:name => nil, :value => nil}]
691
762
  end
692
763
 
693
764
  specify "#insert should insert record with default values when empty hash given" do
694
765
  @d.insert({})
695
-
696
- MYSQL_DB.sqls.should == [
697
- "INSERT INTO items () VALUES ()"
698
- ]
699
-
700
- @d.all.should == [
701
- {:name => nil, :value => nil}
702
- ]
766
+ MYSQL_DB.sqls.should == ["INSERT INTO `items` () VALUES ()"]
767
+ @d.all.should == [{:name => nil, :value => nil}]
703
768
  end
704
769
 
705
770
  specify "#insert should insert record with default values when empty array given" do
706
771
  @d.insert []
707
-
708
- MYSQL_DB.sqls.should == [
709
- "INSERT INTO items () VALUES ()"
710
- ]
711
-
712
- @d.all.should == [
713
- {:name => nil, :value => nil}
714
- ]
772
+ MYSQL_DB.sqls.should == ["INSERT INTO `items` () VALUES ()"]
773
+ @d.all.should == [{:name => nil, :value => nil}]
715
774
  end
716
775
 
717
776
  specify "#on_duplicate_key_update should work with regular inserts" do
@@ -722,19 +781,75 @@ describe "MySQL::Dataset#insert and related methods" do
722
781
  @d.on_duplicate_key_update(:name, :value => 6).insert(:name => 'def', :value => 2)
723
782
 
724
783
  MYSQL_DB.sqls.length.should == 3
725
- MYSQL_DB.sqls[0].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('abc'|1), (1|'abc')\)\z/
726
- MYSQL_DB.sqls[1].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('abc'|1), (1|'abc')\) ON DUPLICATE KEY UPDATE name=VALUES\(name\), value=6\z/
727
- MYSQL_DB.sqls[2].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('def'|2), (2|'def')\) ON DUPLICATE KEY UPDATE name=VALUES\(name\), value=6\z/
784
+ MYSQL_DB.sqls[0].should =~ /\AINSERT INTO `items` \(`(name|value)`, `(name|value)`\) VALUES \(('abc'|1), (1|'abc')\)\z/
785
+ MYSQL_DB.sqls[1].should =~ /\AINSERT INTO `items` \(`(name|value)`, `(name|value)`\) VALUES \(('abc'|1), (1|'abc')\) ON DUPLICATE KEY UPDATE `name`=VALUES\(`name`\), `value`=6\z/
786
+ MYSQL_DB.sqls[2].should =~ /\AINSERT INTO `items` \(`(name|value)`, `(name|value)`\) VALUES \(('def'|2), (2|'def')\) ON DUPLICATE KEY UPDATE `name`=VALUES\(`name`\), `value`=6\z/
728
787
 
729
788
  @d.all.should == [{:name => 'abc', :value => 6}, {:name => 'def', :value => 2}]
730
789
  end
731
790
 
791
+ specify "#multi_replace should insert multiple records in a single statement" do
792
+ @d.multi_replace([{:name => 'abc'}, {:name => 'def'}])
793
+
794
+ MYSQL_DB.sqls.should == [
795
+ SQL_BEGIN,
796
+ "REPLACE INTO `items` (`name`) VALUES ('abc'), ('def')",
797
+ SQL_COMMIT
798
+ ]
799
+
800
+ @d.all.should == [
801
+ {:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
802
+ ]
803
+ end
804
+
805
+ specify "#multi_replace should split the list of records into batches if :commit_every option is given" do
806
+ @d.multi_replace([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
807
+ :commit_every => 2)
808
+
809
+ MYSQL_DB.sqls.should == [
810
+ SQL_BEGIN,
811
+ "REPLACE INTO `items` (`value`) VALUES (1), (2)",
812
+ SQL_COMMIT,
813
+ SQL_BEGIN,
814
+ "REPLACE INTO `items` (`value`) VALUES (3), (4)",
815
+ SQL_COMMIT
816
+ ]
817
+
818
+ @d.all.should == [
819
+ {:name => nil, :value => 1},
820
+ {:name => nil, :value => 2},
821
+ {:name => nil, :value => 3},
822
+ {:name => nil, :value => 4}
823
+ ]
824
+ end
825
+
826
+ specify "#multi_replace should split the list of records into batches if :slice option is given" do
827
+ @d.multi_replace([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
828
+ :slice => 2)
829
+
830
+ MYSQL_DB.sqls.should == [
831
+ SQL_BEGIN,
832
+ "REPLACE INTO `items` (`value`) VALUES (1), (2)",
833
+ SQL_COMMIT,
834
+ SQL_BEGIN,
835
+ "REPLACE INTO `items` (`value`) VALUES (3), (4)",
836
+ SQL_COMMIT
837
+ ]
838
+
839
+ @d.all.should == [
840
+ {:name => nil, :value => 1},
841
+ {:name => nil, :value => 2},
842
+ {:name => nil, :value => 3},
843
+ {:name => nil, :value => 4}
844
+ ]
845
+ end
846
+
732
847
  specify "#multi_insert should insert multiple records in a single statement" do
733
848
  @d.multi_insert([{:name => 'abc'}, {:name => 'def'}])
734
-
849
+
735
850
  MYSQL_DB.sqls.should == [
736
851
  SQL_BEGIN,
737
- "INSERT INTO items (name) VALUES ('abc'), ('def')",
852
+ "INSERT INTO `items` (`name`) VALUES ('abc'), ('def')",
738
853
  SQL_COMMIT
739
854
  ]
740
855
 
@@ -749,13 +864,13 @@ describe "MySQL::Dataset#insert and related methods" do
749
864
 
750
865
  MYSQL_DB.sqls.should == [
751
866
  SQL_BEGIN,
752
- "INSERT INTO items (value) VALUES (1), (2)",
867
+ "INSERT INTO `items` (`value`) VALUES (1), (2)",
753
868
  SQL_COMMIT,
754
869
  SQL_BEGIN,
755
- "INSERT INTO items (value) VALUES (3), (4)",
870
+ "INSERT INTO `items` (`value`) VALUES (3), (4)",
756
871
  SQL_COMMIT
757
872
  ]
758
-
873
+
759
874
  @d.all.should == [
760
875
  {:name => nil, :value => 1},
761
876
  {:name => nil, :value => 2},
@@ -770,13 +885,13 @@ describe "MySQL::Dataset#insert and related methods" do
770
885
 
771
886
  MYSQL_DB.sqls.should == [
772
887
  SQL_BEGIN,
773
- "INSERT INTO items (value) VALUES (1), (2)",
888
+ "INSERT INTO `items` (`value`) VALUES (1), (2)",
774
889
  SQL_COMMIT,
775
890
  SQL_BEGIN,
776
- "INSERT INTO items (value) VALUES (3), (4)",
891
+ "INSERT INTO `items` (`value`) VALUES (3), (4)",
777
892
  SQL_COMMIT
778
893
  ]
779
-
894
+
780
895
  @d.all.should == [
781
896
  {:name => nil, :value => 1},
782
897
  {:name => nil, :value => 2},
@@ -784,28 +899,28 @@ describe "MySQL::Dataset#insert and related methods" do
784
899
  {:name => nil, :value => 4}
785
900
  ]
786
901
  end
787
-
902
+
788
903
  specify "#import should support inserting using columns and values arrays" do
789
904
  @d.import([:name, :value], [['abc', 1], ['def', 2]])
790
905
 
791
906
  MYSQL_DB.sqls.should == [
792
907
  SQL_BEGIN,
793
- "INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2)",
908
+ "INSERT INTO `items` (`name`, `value`) VALUES ('abc', 1), ('def', 2)",
794
909
  SQL_COMMIT
795
910
  ]
796
-
911
+
797
912
  @d.all.should == [
798
913
  {:name => 'abc', :value => 1},
799
914
  {:name => 'def', :value => 2}
800
915
  ]
801
916
  end
802
-
917
+
803
918
  specify "#insert_ignore should add the IGNORE keyword when inserting" do
804
919
  @d.insert_ignore.multi_insert([{:name => 'abc'}, {:name => 'def'}])
805
-
920
+
806
921
  MYSQL_DB.sqls.should == [
807
922
  SQL_BEGIN,
808
- "INSERT IGNORE INTO items (name) VALUES ('abc'), ('def')",
923
+ "INSERT IGNORE INTO `items` (`name`) VALUES ('abc'), ('def')",
809
924
  SQL_COMMIT
810
925
  ]
811
926
 
@@ -813,20 +928,20 @@ describe "MySQL::Dataset#insert and related methods" do
813
928
  {:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
814
929
  ]
815
930
  end
816
-
931
+
817
932
  specify "#insert_ignore should add the IGNORE keyword for single inserts" do
818
933
  @d.insert_ignore.insert(:name => 'ghi')
819
- MYSQL_DB.sqls.should == ["INSERT IGNORE INTO items (name) VALUES ('ghi')"]
934
+ MYSQL_DB.sqls.should == ["INSERT IGNORE INTO `items` (`name`) VALUES ('ghi')"]
820
935
  @d.all.should == [{:name => 'ghi', :value => nil}]
821
936
  end
822
-
937
+
823
938
  specify "#on_duplicate_key_update should add the ON DUPLICATE KEY UPDATE and ALL columns when no args given" do
824
939
  @d.on_duplicate_key_update.import([:name,:value], [['abc', 1], ['def',2]])
825
-
940
+
826
941
  MYSQL_DB.sqls.should == [
827
- "SELECT * FROM items LIMIT 1",
942
+ "SELECT * FROM `items` LIMIT 1",
828
943
  SQL_BEGIN,
829
- "INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)",
944
+ "INSERT INTO `items` (`name`, `value`) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE `name`=VALUES(`name`), `value`=VALUES(`value`)",
830
945
  SQL_COMMIT
831
946
  ]
832
947
 
@@ -834,15 +949,15 @@ describe "MySQL::Dataset#insert and related methods" do
834
949
  {:name => 'abc', :value => 1}, {:name => 'def', :value => 2}
835
950
  ]
836
951
  end
837
-
952
+
838
953
  specify "#on_duplicate_key_update should add the ON DUPLICATE KEY UPDATE and columns specified when args are given" do
839
954
  @d.on_duplicate_key_update(:value).import([:name,:value],
840
955
  [['abc', 1], ['def',2]]
841
956
  )
842
-
957
+
843
958
  MYSQL_DB.sqls.should == [
844
959
  SQL_BEGIN,
845
- "INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE value=VALUES(value)",
960
+ "INSERT INTO `items` (`name`, `value`) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)",
846
961
  SQL_COMMIT
847
962
  ]
848
963
 
@@ -850,7 +965,27 @@ describe "MySQL::Dataset#insert and related methods" do
850
965
  {:name => 'abc', :value => 1}, {:name => 'def', :value => 2}
851
966
  ]
852
967
  end
853
-
968
+
969
+ end
970
+
971
+ describe "MySQL::Dataset#update and related methods" do
972
+ before do
973
+ MYSQL_DB.create_table(:items){String :name; Integer :value; index :name, :unique=>true}
974
+ @d = MYSQL_DB[:items]
975
+ end
976
+ after do
977
+ MYSQL_DB.drop_table?(:items)
978
+ end
979
+
980
+ specify "#update_ignore should not raise error where normal update would fail" do
981
+ @d.insert(:name => 'cow', :value => 0)
982
+ @d.insert(:name => 'cat', :value => 1)
983
+ proc{@d.where(:value => 1).update(:name => 'cow')}.should raise_error(Sequel::DatabaseError)
984
+ MYSQL_DB.sqls.clear
985
+ @d.update_ignore.where(:value => 1).update(:name => 'cow')
986
+ MYSQL_DB.sqls.should == ["UPDATE IGNORE `items` SET `name` = 'cow' WHERE (`value` = 1)"]
987
+ @d.order(:name).all.should == [{:name => 'cat', :value => 1}, {:name => 'cow', :value => 0}]
988
+ end
854
989
  end
855
990
 
856
991
  describe "MySQL::Dataset#replace" do
@@ -860,9 +995,9 @@ describe "MySQL::Dataset#replace" do
860
995
  MYSQL_DB.sqls.clear
861
996
  end
862
997
  after do
863
- MYSQL_DB.drop_table(:items)
998
+ MYSQL_DB.drop_table?(:items)
864
999
  end
865
-
1000
+
866
1001
  specify "should use default values if they exist" do
867
1002
  MYSQL_DB.alter_table(:items){set_column_default :id, 1; set_column_default :value, 2}
868
1003
  @d.replace
@@ -872,7 +1007,7 @@ describe "MySQL::Dataset#replace" do
872
1007
  @d.replace({})
873
1008
  @d.all.should == [{:id=>1, :value=>2}]
874
1009
  end
875
-
1010
+
876
1011
  specify "should use support arrays, datasets, and multiple values" do
877
1012
  @d.replace([1, 2])
878
1013
  @d.all.should == [{:id=>1, :value=>2}]
@@ -881,12 +1016,12 @@ describe "MySQL::Dataset#replace" do
881
1016
  @d.replace(@d)
882
1017
  @d.all.should == [{:id=>1, :value=>2}]
883
1018
  end
884
-
1019
+
885
1020
  specify "should create a record if the condition is not met" do
886
1021
  @d.replace(:id => 111, :value => 333)
887
1022
  @d.all.should == [{:id => 111, :value => 333}]
888
1023
  end
889
-
1024
+
890
1025
  specify "should update a record if the condition is met" do
891
1026
  @d << {:id => 111}
892
1027
  @d.all.should == [{:id => 111, :value => nil}]
@@ -901,25 +1036,25 @@ describe "MySQL::Dataset#complex_expression_sql" do
901
1036
  end
902
1037
 
903
1038
  specify "should handle pattern matches correctly" do
904
- @d.literal(:x.like('a')).should == "(x LIKE BINARY 'a')"
905
- @d.literal(~:x.like('a')).should == "(x NOT LIKE BINARY 'a')"
906
- @d.literal(:x.ilike('a')).should == "(x LIKE 'a')"
907
- @d.literal(~:x.ilike('a')).should == "(x NOT LIKE 'a')"
908
- @d.literal(:x.like(/a/)).should == "(x REGEXP BINARY 'a')"
909
- @d.literal(~:x.like(/a/)).should == "(x NOT REGEXP BINARY 'a')"
910
- @d.literal(:x.like(/a/i)).should == "(x REGEXP 'a')"
911
- @d.literal(~:x.like(/a/i)).should == "(x NOT REGEXP 'a')"
1039
+ @d.literal(:x.like('a')).should == "(`x` LIKE BINARY 'a')"
1040
+ @d.literal(~:x.like('a')).should == "(`x` NOT LIKE BINARY 'a')"
1041
+ @d.literal(:x.ilike('a')).should == "(`x` LIKE 'a')"
1042
+ @d.literal(~:x.ilike('a')).should == "(`x` NOT LIKE 'a')"
1043
+ @d.literal(:x.like(/a/)).should == "(`x` REGEXP BINARY 'a')"
1044
+ @d.literal(~:x.like(/a/)).should == "(`x` NOT REGEXP BINARY 'a')"
1045
+ @d.literal(:x.like(/a/i)).should == "(`x` REGEXP 'a')"
1046
+ @d.literal(~:x.like(/a/i)).should == "(`x` NOT REGEXP 'a')"
912
1047
  end
913
1048
 
914
1049
  specify "should handle string concatenation with CONCAT if more than one record" do
915
- @d.literal([:x, :y].sql_string_join).should == "CONCAT(x, y)"
916
- @d.literal([:x, :y].sql_string_join(' ')).should == "CONCAT(x, ' ', y)"
917
- @d.literal([:x.sql_function(:y), 1, 'z'.lit].sql_string_join(:y.sql_subscript(1))).should == "CONCAT(x(y), y[1], '1', y[1], z)"
1050
+ @d.literal([:x, :y].sql_string_join).should == "CONCAT(`x`, `y`)"
1051
+ @d.literal([:x, :y].sql_string_join(' ')).should == "CONCAT(`x`, ' ', `y`)"
1052
+ @d.literal([:x.sql_function(:y), 1, 'z'.lit].sql_string_join(:y.sql_subscript(1))).should == "CONCAT(x(`y`), `y`[1], '1', `y`[1], z)"
918
1053
  end
919
1054
 
920
1055
  specify "should handle string concatenation as simple string if just one record" do
921
- @d.literal([:x].sql_string_join).should == "x"
922
- @d.literal([:x].sql_string_join(' ')).should == "x"
1056
+ @d.literal([:x].sql_string_join).should == "`x`"
1057
+ @d.literal([:x].sql_string_join(' ')).should == "`x`"
923
1058
  end
924
1059
  end
925
1060
 
@@ -928,12 +1063,12 @@ describe "MySQL::Dataset#calc_found_rows" do
928
1063
  MYSQL_DB.create_table!(:items){Integer :a}
929
1064
  end
930
1065
  after do
931
- MYSQL_DB.drop_table(:items)
1066
+ MYSQL_DB.drop_table?(:items)
932
1067
  end
933
1068
 
934
1069
  specify "should add the SQL_CALC_FOUND_ROWS keyword when selecting" do
935
1070
  MYSQL_DB[:items].select(:a).calc_found_rows.limit(1).sql.should == \
936
- 'SELECT SQL_CALC_FOUND_ROWS a FROM items LIMIT 1'
1071
+ 'SELECT SQL_CALC_FOUND_ROWS `a` FROM `items` LIMIT 1'
937
1072
  end
938
1073
 
939
1074
  specify "should count matching rows disregarding LIMIT clause" do
@@ -944,13 +1079,13 @@ describe "MySQL::Dataset#calc_found_rows" do
944
1079
  MYSQL_DB.dataset.select(:FOUND_ROWS.sql_function.as(:rows)).all.should == [{:rows => 2 }]
945
1080
 
946
1081
  MYSQL_DB.sqls.should == [
947
- 'SELECT SQL_CALC_FOUND_ROWS * FROM items WHERE (a = 1) LIMIT 1',
948
- 'SELECT FOUND_ROWS() AS rows',
1082
+ 'SELECT SQL_CALC_FOUND_ROWS * FROM `items` WHERE (`a` = 1) LIMIT 1',
1083
+ 'SELECT FOUND_ROWS() AS `rows`',
949
1084
  ]
950
1085
  end
951
1086
  end
952
1087
 
953
- if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
1088
+ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc or MYSQL_DB.adapter_scheme == :mysql2
954
1089
  describe "MySQL Stored Procedures" do
955
1090
  before do
956
1091
  MYSQL_DB.create_table(:items){Integer :id; Integer :value}
@@ -958,10 +1093,10 @@ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
958
1093
  MYSQL_DB.sqls.clear
959
1094
  end
960
1095
  after do
961
- MYSQL_DB.drop_table(:items)
1096
+ MYSQL_DB.drop_table?(:items)
962
1097
  MYSQL_DB.execute('DROP PROCEDURE test_sproc')
963
1098
  end
964
-
1099
+
965
1100
  specify "should be callable on the database object" do
966
1101
  MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc() BEGIN DELETE FROM items; END')
967
1102
  MYSQL_DB[:items].delete
@@ -970,27 +1105,38 @@ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
970
1105
  MYSQL_DB.call_sproc(:test_sproc)
971
1106
  MYSQL_DB[:items].count.should == 0
972
1107
  end
973
-
974
- specify "should be callable on the dataset object" do
975
- MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
976
- MYSQL_DB[:items].delete
977
- @d = MYSQL_DB[:items]
978
- @d.call_sproc(:select, :test_sproc, 3).should == []
979
- @d.insert(:value=>1)
980
- @d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
981
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
982
- @d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
1108
+
1109
+ # Mysql2 doesn't support stored procedures that return result sets, probably because
1110
+ # CLIENT_MULTI_RESULTS is not set.
1111
+ unless MYSQL_DB.adapter_scheme == :mysql2
1112
+ specify "should be callable on the dataset object" do
1113
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
1114
+ MYSQL_DB[:items].delete
1115
+ @d = MYSQL_DB[:items]
1116
+ @d.call_sproc(:select, :test_sproc, 3).should == []
1117
+ @d.insert(:value=>1)
1118
+ @d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
1119
+ @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
1120
+ @d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
1121
+ end
1122
+
1123
+ specify "should be callable on the dataset object with multiple arguments" do
1124
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
1125
+ MYSQL_DB[:items].delete
1126
+ @d = MYSQL_DB[:items]
1127
+ @d.call_sproc(:select, :test_sproc, 3, 4).should == []
1128
+ @d.insert(:value=>1)
1129
+ @d.call_sproc(:select, :test_sproc, 4, 5).should == [{:id=>nil, :value=>1, :b=>4, :d=>5}]
1130
+ @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
1131
+ @d.call_sproc(:select, :test_sproc, 3, 4).should == [{:id=>nil, :value=>2, :b=>6, :d => 8}]
1132
+ end
983
1133
  end
984
-
985
- specify "should be callable on the dataset object with multiple arguments" do
986
- MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
1134
+
1135
+ specify "should deal with nil values" do
1136
+ MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(i INTEGER, v INTEGER) BEGIN INSERT INTO items VALUES (i, v); END')
987
1137
  MYSQL_DB[:items].delete
988
- @d = MYSQL_DB[:items]
989
- @d.call_sproc(:select, :test_sproc, 3, 4).should == []
990
- @d.insert(:value=>1)
991
- @d.call_sproc(:select, :test_sproc, 4, 5).should == [{:id=>nil, :value=>1, :b=>4, :d=>5}]
992
- @d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
993
- @d.call_sproc(:select, :test_sproc, 3, 4).should == [{:id=>nil, :value=>2, :b=>6, :d => 8}]
1138
+ MYSQL_DB.call_sproc(:test_sproc, :args=>[1, nil])
1139
+ MYSQL_DB[:items].all.should == [{:id=>1, :value=>nil}]
994
1140
  end
995
1141
  end
996
1142
  end
@@ -998,35 +1144,35 @@ end
998
1144
  if MYSQL_DB.adapter_scheme == :mysql
999
1145
  describe "MySQL bad date/time conversions" do
1000
1146
  after do
1001
- Sequel::MySQL.convert_invalid_date_time = false
1147
+ MYSQL_DB.convert_invalid_date_time = false
1002
1148
  end
1003
-
1149
+
1004
1150
  specify "should raise an exception when a bad date/time is used and convert_invalid_date_time is false" do
1005
- Sequel::MySQL.convert_invalid_date_time = false
1151
+ MYSQL_DB.convert_invalid_date_time = false
1006
1152
  proc{MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value}.should raise_error(Sequel::InvalidValue)
1007
1153
  proc{MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value}.should raise_error(Sequel::InvalidValue)
1008
1154
  proc{MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value}.should raise_error(Sequel::InvalidValue)
1009
1155
  end
1010
-
1156
+
1011
1157
  specify "should not use a nil value bad date/time is used and convert_invalid_date_time is nil or :nil" do
1012
- Sequel::MySQL.convert_invalid_date_time = nil
1158
+ MYSQL_DB.convert_invalid_date_time = nil
1013
1159
  MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value.should == nil
1014
1160
  MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value.should == nil
1015
1161
  MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value.should == nil
1016
- Sequel::MySQL.convert_invalid_date_time = :nil
1162
+ MYSQL_DB.convert_invalid_date_time = :nil
1017
1163
  MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value.should == nil
1018
1164
  MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value.should == nil
1019
1165
  MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value.should == nil
1020
1166
  end
1021
-
1167
+
1022
1168
  specify "should not use a nil value bad date/time is used and convert_invalid_date_time is :string" do
1023
- Sequel::MySQL.convert_invalid_date_time = :string
1169
+ MYSQL_DB.convert_invalid_date_time = :string
1024
1170
  MYSQL_DB["SELECT CAST('0000-00-00' AS date)"].single_value.should == '0000-00-00'
1025
1171
  MYSQL_DB["SELECT CAST('0000-00-00 00:00:00' AS datetime)"].single_value.should == '0000-00-00 00:00:00'
1026
1172
  MYSQL_DB["SELECT CAST('25:00:00' AS time)"].single_value.should == '25:00:00'
1027
1173
  end
1028
1174
  end
1029
-
1175
+
1030
1176
  describe "MySQL multiple result sets" do
1031
1177
  before do
1032
1178
  MYSQL_DB.create_table!(:a){Integer :a}
@@ -1038,41 +1184,41 @@ if MYSQL_DB.adapter_scheme == :mysql
1038
1184
  MYSQL_DB[:b].insert(25)
1039
1185
  end
1040
1186
  after do
1041
- MYSQL_DB.drop_table(:a, :b)
1187
+ MYSQL_DB.drop_table?(:a, :b)
1042
1188
  end
1043
-
1189
+
1044
1190
  specify "should combine all results by default" do
1045
1191
  @ds.all.should == [{:a=>10}, {:a=>15}, {:b=>20}, {:b=>25}]
1046
1192
  end
1047
-
1193
+
1048
1194
  specify "should work with Database#run" do
1049
1195
  proc{MYSQL_DB.run('SELECT * FROM a; SELECT * FROM b')}.should_not raise_error
1050
1196
  proc{MYSQL_DB.run('SELECT * FROM a; SELECT * FROM b')}.should_not raise_error
1051
1197
  end
1052
-
1198
+
1053
1199
  specify "should work with Database#run and other statements" do
1054
1200
  proc{MYSQL_DB.run('UPDATE a SET a = 1; SELECT * FROM a; DELETE FROM b')}.should_not raise_error
1055
1201
  MYSQL_DB[:a].select_order_map(:a).should == [1, 1]
1056
1202
  MYSQL_DB[:b].all.should == []
1057
1203
  end
1058
-
1204
+
1059
1205
  specify "should split results returned into arrays if split_multiple_result_sets is used" do
1060
1206
  @ds.split_multiple_result_sets.all.should == [[{:a=>10}, {:a=>15}], [{:b=>20}, {:b=>25}]]
1061
1207
  end
1062
-
1208
+
1063
1209
  specify "should have regular row_procs work when splitting multiple result sets" do
1064
1210
  @ds.row_proc = proc{|x| x[x.keys.first] *= 2; x}
1065
1211
  @ds.split_multiple_result_sets.all.should == [[{:a=>20}, {:a=>30}], [{:b=>40}, {:b=>50}]]
1066
1212
  end
1067
-
1213
+
1068
1214
  specify "should use the columns from the first result set when splitting result sets" do
1069
1215
  @ds.split_multiple_result_sets.columns.should == [:a]
1070
1216
  end
1071
-
1217
+
1072
1218
  specify "should not allow graphing a dataset that splits multiple statements" do
1073
1219
  proc{@ds.split_multiple_result_sets.graph(:b, :b=>:a)}.should raise_error(Sequel::Error)
1074
1220
  end
1075
-
1221
+
1076
1222
  specify "should not allow splitting a graphed dataset" do
1077
1223
  proc{MYSQL_DB[:a].graph(:b, :b=>:a).split_multiple_result_sets}.should raise_error(Sequel::Error)
1078
1224
  end