sequel 3.21.0 → 3.41.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 (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -0,0 +1,118 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ shared_examples_for "Sequel::ConnectionValidator" do
4
+ before do
5
+ @db.extend(Module.new do
6
+ def disconnect_connection(conn)
7
+ @sqls << 'disconnect'
8
+ end
9
+ def valid_connection?(conn)
10
+ super
11
+ conn.valid
12
+ end
13
+ def connect(server)
14
+ conn = super
15
+ conn.extend(Module.new do
16
+ attr_accessor :valid
17
+ end)
18
+ conn.valid = true
19
+ conn
20
+ end
21
+ end)
22
+ @db.extension(:connection_validator)
23
+ end
24
+
25
+ it "should still allow new connections" do
26
+ @db.synchronize{|c| c}.should be_a_kind_of(Sequel::Mock::Connection)
27
+ end
28
+
29
+ it "should only validate if connection idle longer than timeout" do
30
+ c1 = @db.synchronize{|c| c}
31
+ @db.sqls.should == []
32
+ @db.synchronize{|c| c}.should equal(c1)
33
+ @db.sqls.should == []
34
+ @db.pool.connection_validation_timeout = -1
35
+ @db.synchronize{|c| c}.should equal(c1)
36
+ @db.sqls.should == ['SELECT NULL']
37
+ @db.pool.connection_validation_timeout = 1
38
+ @db.synchronize{|c| c}.should equal(c1)
39
+ @db.sqls.should == []
40
+ @db.synchronize{|c| c}.should equal(c1)
41
+ @db.sqls.should == []
42
+ end
43
+
44
+ it "should disconnect connection if not valid" do
45
+ c1 = @db.synchronize{|c| c}
46
+ @db.sqls.should == []
47
+ c1.valid = false
48
+ @db.pool.connection_validation_timeout = -1
49
+ c2 = @db.synchronize{|c| c}
50
+ @db.sqls.should == ['SELECT NULL', 'disconnect']
51
+ c2.should_not equal(c1)
52
+ end
53
+
54
+ it "should disconnect multiple connections repeatedly if they are not valid" do
55
+ q, q1 = Queue.new, Queue.new
56
+ c1 = nil
57
+ c2 = nil
58
+ @db.pool.connection_validation_timeout = -1
59
+ @db.synchronize do |c|
60
+ Thread.new do
61
+ @db.synchronize do |cc|
62
+ c2 = cc
63
+ end
64
+ q1.pop
65
+ q.push nil
66
+ end
67
+ q1.push nil
68
+ q.pop
69
+ c1 = c
70
+ end
71
+ c1.valid = false
72
+ c2.valid = false
73
+
74
+ c3 = @db.synchronize{|c| c}
75
+ @db.sqls.should == ['SELECT NULL', 'disconnect', 'SELECT NULL', 'disconnect']
76
+ c3.should_not equal(c1)
77
+ c3.should_not equal(c2)
78
+ end
79
+
80
+ it "should not leak connection references" do
81
+ c1 = @db.synchronize do |c|
82
+ @db.pool.instance_variable_get(:@connection_timestamps).should == {}
83
+ c
84
+ end
85
+ @db.pool.instance_variable_get(:@connection_timestamps).should have_key(c1)
86
+
87
+ c1.valid = false
88
+ @db.pool.connection_validation_timeout = -1
89
+ c2 = @db.synchronize do |c|
90
+ @db.pool.instance_variable_get(:@connection_timestamps).should == {}
91
+ c
92
+ end
93
+ c2.should_not equal(c1)
94
+ @db.pool.instance_variable_get(:@connection_timestamps).should_not have_key(c1)
95
+ @db.pool.instance_variable_get(:@connection_timestamps).should have_key(c2)
96
+ end
97
+
98
+ it "should handle case where determining validity requires a connection" do
99
+ @db.meta_def(:valid_connection?){|c| synchronize{}; true}
100
+ @db.pool.connection_validation_timeout = -1
101
+ c1 = @db.synchronize{|c| c}
102
+ @db.synchronize{|c| c}.should equal(c1)
103
+ end
104
+ end
105
+
106
+ describe "Sequel::ConnectionValidator with threaded pool" do
107
+ before do
108
+ @db = Sequel.mock
109
+ end
110
+ it_should_behave_like "Sequel::ConnectionValidator"
111
+ end
112
+ describe "Sequel::ConnectionValidator with sharded threaded pool" do
113
+ before do
114
+ @db = Sequel.mock(:servers=>{})
115
+ end
116
+ it_should_behave_like "Sequel::ConnectionValidator"
117
+ end
118
+
@@ -0,0 +1,196 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::ConstraintValidations" do
4
+ def model_class(opts={})
5
+ return @c if @c
6
+ @c = Class.new(Sequel::Model(@db[:items]))
7
+ @c.columns :name
8
+ @db.sqls
9
+ set_fetch(opts)
10
+ @c.plugin :constraint_validations
11
+ @c
12
+ end
13
+
14
+ def set_fetch(opts)
15
+ @db.fetch = {:table=>'items', :message=>nil, :allow_nil=>nil, :constraint_name=>nil, :validation_type=>'presence', :argument=>nil, :column=>'name'}.merge(opts)
16
+ end
17
+
18
+ before do
19
+ @db = Sequel.mock
20
+ set_fetch({})
21
+ @ds = @db[:items]
22
+ @ds.instance_variable_set(:@columns, [:name])
23
+ @ds2 = @db.dup[:items2]
24
+ @ds2.instance_variable_set(:@columns, [:name])
25
+ end
26
+
27
+ it "should load the validation_helpers plugin into the class" do
28
+ model_class.new.should respond_to(:validates_presence)
29
+ end
30
+
31
+ it "should parse constraint validations when loading plugin" do
32
+ @c = model_class
33
+ @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
34
+ @db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
35
+ @c.constraint_validations.should == [[:validates_presence, :name]]
36
+ end
37
+
38
+ it "should parse constraint validations with a custom constraint validations table" do
39
+ c = Class.new(Sequel::Model(@db[:items]))
40
+ @db.sqls
41
+ c.plugin :constraint_validations, :constraint_validations_table=>:foo
42
+ @db.sqls.should == ["SELECT * FROM foo"]
43
+ @db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
44
+ c.constraint_validations.should == [[:validates_presence, :name]]
45
+ end
46
+
47
+ it "should populate constraint_validations when subclassing" do
48
+ c = Class.new(Sequel::Model(@db))
49
+ c.plugin :constraint_validations
50
+ @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
51
+ sc = Class.new(c)
52
+ sc.set_dataset @ds
53
+ @db.sqls.should == []
54
+ sc.constraint_validations.should == [[:validates_presence, :name]]
55
+ end
56
+
57
+ it "should populate constraint_validations when changing the model's dataset" do
58
+ c = Class.new(Sequel::Model(@db[:foo]))
59
+ c.columns :name
60
+ @db.sqls
61
+ c.plugin :constraint_validations
62
+ @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
63
+ sc = Class.new(c)
64
+ sc.set_dataset @ds
65
+ @db.sqls.should == []
66
+ sc.constraint_validations.should == [[:validates_presence, :name]]
67
+ end
68
+
69
+ it "should reparse constraint validations when changing the model's database" do
70
+ c = Class.new(Sequel::Model(@ds2))
71
+ c.plugin :constraint_validations
72
+ @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
73
+ sc = Class.new(c)
74
+ sc.set_dataset @ds
75
+ @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
76
+ sc.constraint_validations.should == [[:validates_presence, :name]]
77
+ end
78
+
79
+ it "should reparse constraint validations when changing the model's database with a custom constraint validations table" do
80
+ c = Class.new(Sequel::Model(@ds2))
81
+ c.plugin :constraint_validations, :constraint_validations_table=>:foo
82
+ @db.sqls.should == ["SELECT * FROM foo"]
83
+ sc = Class.new(c)
84
+ sc.set_dataset @ds
85
+ @db.sqls.should == ["SELECT * FROM foo"]
86
+ sc.constraint_validations.should == [[:validates_presence, :name]]
87
+ end
88
+
89
+ it "should correctly retrieve :message option from constraint validations table" do
90
+ model_class(:message=>'foo').constraint_validations.should == [[:validates_presence, :name, {:message=>'foo'}]]
91
+ end
92
+
93
+ it "should correctly retrieve :allow_nil option from constraint validations table" do
94
+ model_class(:allow_nil=>true).constraint_validations.should == [[:validates_presence, :name, {:allow_nil=>true}]]
95
+ end
96
+
97
+ it "should handle presence validation" do
98
+ model_class(:validation_type=>'presence').constraint_validations.should == [[:validates_presence, :name]]
99
+ end
100
+
101
+ it "should handle exact_length validation" do
102
+ model_class(:validation_type=>'exact_length', :argument=>'5').constraint_validations.should == [[:validates_exact_length, 5, :name]]
103
+ end
104
+
105
+ it "should handle min_length validation" do
106
+ model_class(:validation_type=>'min_length', :argument=>'5').constraint_validations.should == [[:validates_min_length, 5, :name]]
107
+ end
108
+
109
+ it "should handle max_length validation" do
110
+ model_class(:validation_type=>'max_length', :argument=>'5').constraint_validations.should == [[:validates_max_length, 5, :name]]
111
+ end
112
+
113
+ it "should handle length_range validation" do
114
+ model_class(:validation_type=>'length_range', :argument=>'3..5').constraint_validations.should == [[:validates_length_range, 3..5, :name]]
115
+ end
116
+
117
+ it "should handle length_range validation with an exclusive end" do
118
+ model_class(:validation_type=>'length_range', :argument=>'3...5').constraint_validations.should == [[:validates_length_range, 3...5, :name]]
119
+ end
120
+
121
+ it "should handle format validation" do
122
+ model_class(:validation_type=>'format', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/, :name]]
123
+ end
124
+
125
+ it "should handle format validation with case insensitive format" do
126
+ model_class(:validation_type=>'iformat', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/i, :name]]
127
+ end
128
+
129
+ it "should handle includes validation with array of strings" do
130
+ model_class(:validation_type=>'includes_str_array', :argument=>'a,b,c').constraint_validations.should == [[:validates_includes, %w'a b c', :name]]
131
+ end
132
+
133
+ it "should handle includes validation with array of integers" do
134
+ model_class(:validation_type=>'includes_int_array', :argument=>'1,2,3').constraint_validations.should == [[:validates_includes, [1, 2, 3], :name]]
135
+ end
136
+
137
+ it "should handle includes validation with inclusive range of integers" do
138
+ model_class(:validation_type=>'includes_int_range', :argument=>'3..5').constraint_validations.should == [[:validates_includes, 3..5, :name]]
139
+ end
140
+
141
+ it "should handle includes validation with exclusive range of integers" do
142
+ model_class(:validation_type=>'includes_int_range', :argument=>'3...5').constraint_validations.should == [[:validates_includes, 3...5, :name]]
143
+ end
144
+
145
+ it "should handle like validation" do
146
+ model_class(:validation_type=>'like', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/, :name]]
147
+ end
148
+
149
+ it "should handle ilike validation" do
150
+ model_class(:validation_type=>'ilike', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/i, :name]]
151
+ end
152
+
153
+ it "should handle like validation with % metacharacter" do
154
+ model_class(:validation_type=>'like', :argument=>'%foo%').constraint_validations.should == [[:validates_format, /\A.*foo.*\z/, :name]]
155
+ end
156
+
157
+ it "should handle like validation with %% metacharacter" do
158
+ model_class(:validation_type=>'like', :argument=>'%%foo%%').constraint_validations.should == [[:validates_format, /\A%foo%\z/, :name]]
159
+ end
160
+
161
+ it "should handle like validation with _ metacharacter" do
162
+ model_class(:validation_type=>'like', :argument=>'f_o').constraint_validations.should == [[:validates_format, /\Af.o\z/, :name]]
163
+ end
164
+
165
+ it "should handle like validation with Regexp metacharacter" do
166
+ model_class(:validation_type=>'like', :argument=>'\wfoo\d').constraint_validations.should == [[:validates_format, /\A\\wfoo\\d\z/, :name]]
167
+ end
168
+
169
+ it "should handle unique validation" do
170
+ model_class(:validation_type=>'unique').constraint_validations.should == [[:validates_unique, [:name]]]
171
+ end
172
+
173
+ it "should handle unique validation with multiple columns" do
174
+ model_class(:validation_type=>'unique', :column=>'name,id').constraint_validations.should == [[:validates_unique, [:name, :id]]]
175
+ end
176
+
177
+ it "should used parsed constraint validations when validating" do
178
+ o = model_class.new
179
+ o.valid?.should == false
180
+ o.errors.full_messages.should == ['name is not present']
181
+ end
182
+
183
+ it "should handle a table name specified as SQL::Identifier" do
184
+ set_fetch(:table=>'sch__items')
185
+ c = Class.new(Sequel::Model(@db[Sequel.identifier(:sch__items)]))
186
+ c.plugin :constraint_validations
187
+ c.constraint_validations.should == [[:validates_presence, :name]]
188
+ end
189
+
190
+ it "should handle a table name specified as SQL::QualifiedIdentifier" do
191
+ set_fetch(:table=>'sch.items')
192
+ c = Class.new(Sequel::Model(@db[Sequel.qualify(:sch, :items)]))
193
+ c.plugin :constraint_validations
194
+ c.constraint_validations.should == [[:validates_presence, :name]]
195
+ end
196
+ end
@@ -0,0 +1,316 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "constraint_validations extension" do
4
+ def parse_insert(s)
5
+ m = /\AINSERT INTO sequel_constraint_validations \((.*)\) VALUES \((.*)\)\z/.match(s)
6
+ Hash[*m[1].split(', ').map{|v| v.to_sym}.zip(m[2].split(', ').map{|v| parse_insert_value(v)}).reject{|k, v| v.nil?}.flatten]
7
+ end
8
+
9
+ def parse_insert_value(s)
10
+ case s
11
+ when 'NULL'
12
+ nil
13
+ when /\A'(.*)'\z/
14
+ $1
15
+ else
16
+ raise Sequel::Error, "unhandled insert value: #{s.inspect}"
17
+ end
18
+ end
19
+
20
+ before do
21
+ @db = Sequel.mock
22
+ @db.extend(Module.new{attr_writer :schema; def schema(table, *) execute("parse schema for #{table}"); @schema; end})
23
+ @db.extension(:constraint_validations)
24
+ end
25
+
26
+ it "should allow creating the sequel_constraint_validations table" do
27
+ @db.create_constraint_validations_table
28
+ @db.sqls.should == ["CREATE TABLE sequel_constraint_validations (table varchar(255) NOT NULL, constraint_name varchar(255), validation_type varchar(255) NOT NULL, column varchar(255) NOT NULL, argument varchar(255), message varchar(255), allow_nil boolean)"]
29
+ end
30
+
31
+ it "should allow creating the sequel_constraint_validations table with a non-default table name" do
32
+ @db.constraint_validations_table = :foo
33
+ @db.create_constraint_validations_table
34
+ @db.sqls.should == ["CREATE TABLE foo (table varchar(255) NOT NULL, constraint_name varchar(255), validation_type varchar(255) NOT NULL, column varchar(255) NOT NULL, argument varchar(255), message varchar(255), allow_nil boolean)"]
35
+ end
36
+
37
+ it "should allow dropping the sequel_constraint_validations table" do
38
+ @db.drop_constraint_validations_table
39
+ @db.sqls.should == ["DROP TABLE sequel_constraint_validations"]
40
+ end
41
+
42
+ it "should allow dropping the sequel_constraint_validations table with a non-default table name" do
43
+ @db.constraint_validations_table = :foo
44
+ @db.drop_constraint_validations_table
45
+ @db.sqls.should == ["DROP TABLE foo"]
46
+ end
47
+
48
+ it "should allow dropping validations for a given table" do
49
+ @db.drop_constraint_validations_for(:table=>:foo)
50
+ @db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE (table = 'foo')"]
51
+ end
52
+
53
+ it "should allow dropping validations for a given table and column" do
54
+ @db.drop_constraint_validations_for(:table=>:foo, :column=>:bar)
55
+ @db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table = 'foo') AND (column = 'bar'))"]
56
+ end
57
+
58
+ it "should allow dropping validations for a given table and constraint" do
59
+ @db.drop_constraint_validations_for(:table=>:foo, :constraint=>:bar)
60
+ @db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table = 'foo') AND (constraint_name = 'bar'))"]
61
+ end
62
+
63
+ it "should allow dropping validations for a non-default constraint_validations table" do
64
+ @db.constraint_validations_table = :cv
65
+ @db.drop_constraint_validations_for(:table=>:foo)
66
+ @db.sqls.should == ["DELETE FROM cv WHERE (table = 'foo')"]
67
+ end
68
+
69
+ it "should raise an error without deleting if attempting to drop validations with table, column, or constraint" do
70
+ proc{@db.drop_constraint_validations_for({})}.should raise_error(Sequel::Error)
71
+ @db.sqls.should == []
72
+ end
73
+
74
+ it "should allow adding constraint validations via create_table validate" do
75
+ @db.create_table(:foo){String :name; validate{presence :name}}
76
+ sqls = @db.sqls
77
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
78
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
79
+ end
80
+
81
+ it "should allow adding constraint validations via alter_table validate" do
82
+ @db.schema = [[:name, {:type=>:string}]]
83
+ @db.alter_table(:foo){validate{presence :name}}
84
+ sqls = @db.sqls
85
+ parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
86
+ sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK ((name IS NOT NULL) AND (trim(name) != ''))"]
87
+ end
88
+
89
+ it "should handle :message option when adding validations" do
90
+ @db.create_table(:foo){String :name; validate{presence :name, :message=>'not there'}}
91
+ sqls = @db.sqls
92
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :message=>'not there'}
93
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
94
+ end
95
+
96
+ it "should handle :allow_nil option when adding validations" do
97
+ @db.create_table(:foo){String :name; validate{presence :name, :allow_nil=>true}}
98
+ sqls = @db.sqls
99
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t'}
100
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK (trim(name) != ''))"]
101
+ end
102
+
103
+ it "should handle :name option when adding validations" do
104
+ @db.create_table(:foo){String :name; validate{presence :name, :name=>'cons'}}
105
+ sqls = @db.sqls
106
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :constraint_name=>'cons'}
107
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CONSTRAINT cons CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
108
+ end
109
+
110
+ it "should handle multiple columns when adding validations" do
111
+ @db.create_table(:foo){String :name; String :bar; validate{presence [:name, :bar]}}
112
+ sqls = @db.sqls
113
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
114
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
115
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK ((name IS NOT NULL) AND (bar IS NOT NULL) AND (trim(name) != '') AND (trim(bar) != '')))"]
116
+ end
117
+
118
+ it "should handle presence validation on non-String columns" do
119
+ @db.create_table(:foo){Integer :name; validate{presence :name}}
120
+ sqls = @db.sqls
121
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
122
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer, CHECK (name IS NOT NULL))"]
123
+
124
+ @db.schema = [[:name, {:type=>:integer}]]
125
+ @db.alter_table(:foo){validate{presence :name}}
126
+ sqls = @db.sqls
127
+ parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
128
+ sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK (name IS NOT NULL)"]
129
+ end
130
+
131
+ it "should handle presence validation on Oracle with IS NOT NULL instead of != ''" do
132
+ @db = Sequel.mock(:host=>'oracle')
133
+ @db.extension(:constraint_validations)
134
+ @db.create_table(:foo){String :name; validate{presence :name}}
135
+ sqls = @db.sqls
136
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
137
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) IS NOT NULL)))"]
138
+ end
139
+
140
+ it "should assume column is not a String if it can't determine the type" do
141
+ @db.create_table(:foo){Integer :name; validate{presence :bar}}
142
+ sqls = @db.sqls
143
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
144
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer, CHECK (bar IS NOT NULL))"]
145
+
146
+ @db.schema = [[:name, {:type=>:integer}]]
147
+ @db.alter_table(:foo){validate{presence :bar}}
148
+ sqls = @db.sqls
149
+ parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
150
+ sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK (bar IS NOT NULL)"]
151
+ end
152
+
153
+ it "should handle presence validation on non-String columns with :allow_nil option" do
154
+ @db.create_table(:foo){Integer :name; validate{presence :name, :allow_nil=>true}}
155
+ sqls = @db.sqls
156
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t'}
157
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer)"]
158
+ end
159
+
160
+ it "should support :exact_length constraint validation" do
161
+ @db.create_table(:foo){String :name; validate{exact_length 5, :name}}
162
+ sqls = @db.sqls
163
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"exact_length", :column=>"name", :table=>"foo", :argument=>'5'}
164
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) = 5)))"]
165
+ end
166
+
167
+ it "should support :min_length constraint validation" do
168
+ @db.create_table(:foo){String :name; validate{min_length 5, :name}}
169
+ sqls = @db.sqls
170
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"min_length", :column=>"name", :table=>"foo", :argument=>'5'}
171
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 5)))"]
172
+ end
173
+
174
+ it "should support :max_length constraint validation" do
175
+ @db.create_table(:foo){String :name; validate{max_length 5, :name}}
176
+ sqls = @db.sqls
177
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"max_length", :column=>"name", :table=>"foo", :argument=>'5'}
178
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) <= 5)))"]
179
+ end
180
+
181
+ it "should support :length_range constraint validation" do
182
+ @db.create_table(:foo){String :name; validate{length_range 3..5, :name}}
183
+ sqls = @db.sqls
184
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"length_range", :column=>"name", :table=>"foo", :argument=>'3..5'}
185
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 3) AND (char_length(name) <= 5)))"]
186
+
187
+ @db.create_table(:foo){String :name; validate{length_range 3...5, :name}}
188
+ sqls = @db.sqls
189
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"length_range", :column=>"name", :table=>"foo", :argument=>'3...5'}
190
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 3) AND (char_length(name) < 5)))"]
191
+ end
192
+
193
+ it "should support :format constraint validation" do
194
+ @db = Sequel.mock(:host=>'postgres')
195
+ @db.extension(:constraint_validations)
196
+ @db.create_table(:foo){String :name; validate{format /^foo.*/, :name}}
197
+ sqls = @db.sqls
198
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"format", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
199
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~ '^foo.*')))"]
200
+ end
201
+
202
+ it "should support :format constraint validation with case insensitive format" do
203
+ @db = Sequel.mock(:host=>'postgres')
204
+ @db.extension(:constraint_validations)
205
+ @db.create_table(:foo){String :name; validate{format /^foo.*/i, :name}}
206
+ sqls = @db.sqls
207
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"iformat", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
208
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~* '^foo.*')))"]
209
+ end
210
+
211
+ it "should support :includes constraint validation with an array of strings" do
212
+ @db.create_table(:foo){String :name; validate{includes %w'a b c', :name}}
213
+ sqls = @db.sqls
214
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_str_array", :column=>"name", :table=>"foo", :argument=>'a,b,c'}
215
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name IN ('a', 'b', 'c'))))"]
216
+ end
217
+
218
+ it "should support :includes constraint validation with an array of integers" do
219
+ @db.create_table(:foo){String :name; validate{includes [1, 2, 3], :name}}
220
+ sqls = @db.sqls
221
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_array", :column=>"name", :table=>"foo", :argument=>'1,2,3'}
222
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name IN (1, 2, 3))))"]
223
+ end
224
+
225
+ it "should support :includes constraint validation with a inclusive range of integers" do
226
+ @db.create_table(:foo){String :name; validate{includes 3..5, :name}}
227
+ sqls = @db.sqls
228
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_range", :column=>"name", :table=>"foo", :argument=>'3..5'}
229
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name >= 3) AND (name <= 5)))"]
230
+ end
231
+
232
+ it "should support :includes constraint validation with a exclusive range of integers" do
233
+ @db.create_table(:foo){String :name; validate{includes 3...5, :name}}
234
+ sqls = @db.sqls
235
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_range", :column=>"name", :table=>"foo", :argument=>'3...5'}
236
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name >= 3) AND (name < 5)))"]
237
+ end
238
+
239
+ it "should support :like constraint validation" do
240
+ @db.create_table(:foo){String :name; validate{like 'foo%', :name}}
241
+ sqls = @db.sqls
242
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"like", :column=>"name", :table=>"foo", :argument=>'foo%'}
243
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name LIKE 'foo%')))"]
244
+ end
245
+
246
+ it "should support :ilike constraint validation" do
247
+ @db.create_table(:foo){String :name; validate{ilike 'foo%', :name}}
248
+ sqls = @db.sqls
249
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"ilike", :column=>"name", :table=>"foo", :argument=>'foo%'}
250
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name ILIKE 'foo%')))"]
251
+ end
252
+
253
+ it "should support :unique constraint validation" do
254
+ @db.create_table(:foo){String :name; validate{unique :name}}
255
+ sqls = @db.sqls
256
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name", :table=>"foo"}
257
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), UNIQUE (name))"]
258
+ end
259
+
260
+ it "should support :unique constraint validation with multiple columns" do
261
+ @db.create_table(:foo){String :name; Integer :id; validate{unique [:name, :id]}}
262
+ sqls = @db.sqls
263
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name,id", :table=>"foo"}
264
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), id integer, UNIQUE (name, id))"]
265
+ end
266
+
267
+ it "should support :unique constraint validation in alter_table" do
268
+ @db.alter_table(:foo){validate{unique :name}}
269
+ sqls = @db.sqls
270
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name", :table=>"foo"}
271
+ sqls.should == ["BEGIN", "COMMIT", "ALTER TABLE foo ADD UNIQUE (name)"]
272
+ end
273
+
274
+ it "should drop constraints and validations when dropping a constraint validation" do
275
+ @db.alter_table(:foo){String :name; validate{drop :bar}}
276
+ @db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table, constraint_name) IN (('foo', 'bar')))", "ALTER TABLE foo DROP CONSTRAINT bar"]
277
+ end
278
+
279
+ it "should raise an error if attempting to validate inclusion with a range of non-integers" do
280
+ proc{@db.create_table(:foo){String :name; validate{includes 'a'..'z', :name}}}.should raise_error(Sequel::Error)
281
+ end
282
+
283
+ it "should raise an error if attempting to validate inclusion with a range of non-integers or strings" do
284
+ proc{@db.create_table(:foo){String :name; validate{includes [1.0, 2.0], :name}}}.should raise_error(Sequel::Error)
285
+ end
286
+
287
+ it "should raise an error if attempting to validate inclusion with a unsupported object" do
288
+ proc{@db.create_table(:foo){String :name; validate{includes 'a', :name}}}.should raise_error(Sequel::Error)
289
+ end
290
+
291
+ it "should raise an error if attempting to drop a constraint validation in a create_table generator" do
292
+ proc{@db.create_table(:foo){String :name; validate{drop :foo}}}.should raise_error(Sequel::Error)
293
+ end
294
+
295
+ it "should raise an error if attempting to drop a constraint validation without a name" do
296
+ proc{@db.alter_table(:foo){String :name; validate{drop nil}}}.should raise_error(Sequel::Error)
297
+ end
298
+
299
+ it "should raise an error if attempting attempting to process a constraint validation with an unsupported type" do
300
+ proc{@db.alter_table(:foo){String :name; validations << {:type=>:foo}}}.should raise_error(Sequel::Error)
301
+ end
302
+
303
+ it "should allow adding constraint validations for tables specified as a SQL::Identifier" do
304
+ @db.create_table(Sequel.identifier(:sch__foo)){String :name; validate{presence :name}}
305
+ sqls = @db.sqls
306
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"sch__foo"}
307
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE sch__foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
308
+ end
309
+
310
+ it "should allow adding constraint validations for tables specified as a SQL::QualifiedIdentifier" do
311
+ @db.create_table(Sequel.qualify(:sch, :foo)){String :name; validate{presence :name}}
312
+ sqls = @db.sqls
313
+ parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"sch.foo"}
314
+ sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE sch.foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
315
+ end
316
+ end