sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -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