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,85 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "null_dataset extension" do
4
+ before do
5
+ @db = Sequel::mock(:fetch=>{:id=>1}, :autoid=>1, :numrows=>1, :columns=>[:id])
6
+ @ds = @db[:table].nullify
7
+ @i = 0
8
+ @pr = proc{|*a| @i += 1}
9
+ end
10
+ after do
11
+ @db.sqls.should == [] unless @skip_check
12
+ end
13
+
14
+ it "should make each be a noop" do
15
+ @ds.each(&@pr).should equal(@ds)
16
+ @i.should == 0
17
+ end
18
+
19
+ it "should make fetch_rows be a noop" do
20
+ @ds.fetch_rows("SELECT 1", &@pr).should == nil
21
+ @i.should == 0
22
+ end
23
+
24
+ it "should make insert be a noop" do
25
+ @ds.insert(1).should == nil
26
+ end
27
+
28
+ it "should make update be a noop" do
29
+ @ds.update(:a=>1).should == 0
30
+ end
31
+
32
+ it "should make delete be a noop" do
33
+ @ds.delete.should == 0
34
+ end
35
+
36
+ it "should make truncate be a noop" do
37
+ @ds.truncate.should == nil
38
+ end
39
+
40
+ it "should make execute_* be a noop" do
41
+ @ds.send(:execute_ddl,'FOO').should == nil
42
+ @ds.send(:execute_insert,'FOO').should == nil
43
+ @ds.send(:execute_dui,'FOO').should == nil
44
+ @ds.send(:execute,'FOO').should == nil
45
+ end
46
+
47
+ it "should have working columns" do
48
+ @skip_check = true
49
+ @ds.columns.should == [:id]
50
+ @db.sqls.should == ['SELECT * FROM table LIMIT 1']
51
+ end
52
+
53
+ it "should have count return 0" do
54
+ @ds.count.should == 0
55
+ end
56
+
57
+ it "should have empty return true" do
58
+ @ds.empty?.should == true
59
+ end
60
+
61
+ it "should make import a noop" do
62
+ @ds.import([:id], [[1], [2], [3]]).should == nil
63
+ end
64
+
65
+ it "should have nullify method returned modified receiver" do
66
+ @skip_check = true
67
+ ds = @db[:table]
68
+ ds.nullify.should_not equal(ds)
69
+ ds.each(&@pr)
70
+ @db.sqls.should == ['SELECT * FROM table']
71
+ @i.should == 1
72
+ end
73
+
74
+ it "should have null! method modify receiver" do
75
+ ds = @db[:table]
76
+ ds.nullify!.should equal(ds)
77
+ ds.each(&@pr)
78
+ @i.should == 0
79
+ end
80
+
81
+ it "should work with method chaining" do
82
+ @ds.where(:a=>1).select(:b).each(&@pr)
83
+ @i.should == 0
84
+ end
85
+ end
@@ -6,11 +6,8 @@ describe "optimistic_locking plugin" do
6
6
  end
7
7
  h = {1=>{:id=>1, :name=>'John', :lock_version=>2}}
8
8
  lv = @lv = "lock_version"
9
- @c.dataset.quote_identifiers = false
10
- @c.dataset.meta_def(:h){h}
11
- @c.dataset.meta_def(:lv){lv}
12
- @c.dataset.meta_def(:update) do |opts|
13
- case update_sql(opts)
9
+ @c.instance_dataset.numrows = @c.dataset.numrows = proc do |sql|
10
+ case sql
14
11
  when /UPDATE people SET (name|#{lv}) = ('Jim'|'Bob'|\d+), (?:name|#{lv}) = ('Jim'|'Bob'|\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
15
12
  name, nlv = $1 == 'name' ? [$2, $3] : [$3, $2]
16
13
  m = h[$4.to_i]
@@ -28,20 +25,6 @@ describe "optimistic_locking plugin" do
28
25
  else
29
26
  0
30
27
  end
31
- else
32
- puts update_sql(opts)
33
- end
34
- end
35
- @c.dataset.instance_eval do
36
- def fetch_rows(sql)
37
- m = h[1].dup
38
- v = m.delete(:lock_version)
39
- m[lv.to_sym] = v
40
- yield(m)
41
- end
42
- end
43
- @c.dataset.meta_def(:delete) do
44
- case delete_sql
45
28
  when /DELETE FROM people WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
46
29
  m = h[$1.to_i]
47
30
  if m && m[lv.to_sym] == $2.to_i
@@ -51,9 +34,15 @@ describe "optimistic_locking plugin" do
51
34
  0
52
35
  end
53
36
  else
54
- puts delete_sql
37
+ puts sql
55
38
  end
56
39
  end
40
+ @c.instance_dataset._fetch = @c.dataset._fetch = proc do |sql|
41
+ m = h[1].dup
42
+ v = m.delete(:lock_version)
43
+ m[lv.to_sym] = v
44
+ m
45
+ end
57
46
  @c.columns :id, :name, :lock_version
58
47
  @c.plugin :optimistic_locking
59
48
  end
@@ -106,6 +95,20 @@ describe "optimistic_locking plugin" do
106
95
  proc{p2.update(:name=>'Bob')}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
107
96
  end
108
97
 
98
+ specify "should work correctly if attempting to refresh and save again after a failed save" do
99
+ p1 = @c[1]
100
+ p2 = @c[1]
101
+ p1.update(:name=>'Jim')
102
+ begin
103
+ p2.update(:name=>'Bob')
104
+ rescue Sequel::Plugins::OptimisticLocking::Error
105
+ p2.refresh
106
+ @c.db.sqls
107
+ proc{p2.update(:name=>'Bob')}.should_not raise_error
108
+ end
109
+ @c.db.sqls.first.should =~ /UPDATE people SET (name = 'Bob', lock_version = 4|lock_version = 4, name = 'Bob') WHERE \(\(id = 1\) AND \(lock_version = 3\)\)/
110
+ end
111
+
109
112
  specify "should increment the lock column when #modified! even if no columns are changed" do
110
113
  p1 = @c[1]
111
114
  p1.modified!
@@ -115,7 +118,7 @@ describe "optimistic_locking plugin" do
115
118
  end
116
119
 
117
120
  specify "should not increment the lock column when the update fails" do
118
- @c.dataset.meta_def(:update) { raise Exception }
121
+ @c.instance_dataset.meta_def(:update) { raise Exception }
119
122
  p1 = @c[1]
120
123
  p1.modified!
121
124
  lv = p1.lock_version
@@ -0,0 +1,112 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Postgres::ArrayOp" do
4
+ before do
5
+ @db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
6
+ @a = Sequel.pg_array_op(:a)
7
+ end
8
+
9
+ it "should support the standard mathematical operators" do
10
+ @db.literal(@a < @a).should == "(a < a)"
11
+ @db.literal(@a <= @a).should == "(a <= a)"
12
+ @db.literal(@a > @a).should == "(a > a)"
13
+ @db.literal(@a >= @a).should == "(a >= a)"
14
+ end
15
+
16
+ it "#[] should support subscript access" do
17
+ @db.literal(@a[1]).should == "a[1]"
18
+ @db.literal(@a[1][2]).should == "a[1][2]"
19
+ end
20
+
21
+ it "#any should use the ANY method" do
22
+ @db.literal(1=>@a.any).should == "(1 = ANY(a))"
23
+ end
24
+
25
+ it "#all should use the ALL method" do
26
+ @db.literal(1=>@a.all).should == "(1 = ALL(a))"
27
+ end
28
+
29
+ it "#contains should use the @> operator" do
30
+ @db.literal(@a.contains(:b)).should == "(a @> b)"
31
+ end
32
+
33
+ it "#contained_by should use the <@ operator" do
34
+ @db.literal(@a.contained_by(:b)).should == "(a <@ b)"
35
+ end
36
+
37
+ it "#overlaps should use the && operator" do
38
+ @db.literal(@a.overlaps(:b)).should == "(a && b)"
39
+ end
40
+
41
+ it "#push/concat should use the || operator in append mode" do
42
+ @db.literal(@a.push(:b)).should == "(a || b)"
43
+ @db.literal(@a.concat(:b)).should == "(a || b)"
44
+ end
45
+
46
+ it "#unshift should use the || operator in prepend mode" do
47
+ @db.literal(@a.unshift(:b)).should == "(b || a)"
48
+ end
49
+
50
+ it "#dims should use the array_dims function" do
51
+ @db.literal(@a.dims).should == "array_dims(a)"
52
+ end
53
+
54
+ it "#length should use the array_length function" do
55
+ @db.literal(@a.length).should == "array_length(a, 1)"
56
+ @db.literal(@a.length(2)).should == "array_length(a, 2)"
57
+ end
58
+
59
+ it "#length should use the array_lower function" do
60
+ @db.literal(@a.lower).should == "array_lower(a, 1)"
61
+ @db.literal(@a.lower(2)).should == "array_lower(a, 2)"
62
+ end
63
+
64
+ it "#to_string/join should use the array_to_string function" do
65
+ @db.literal(@a.to_string).should == "array_to_string(a, '', NULL)"
66
+ @db.literal(@a.join).should == "array_to_string(a, '', NULL)"
67
+ @db.literal(@a.join(':')).should == "array_to_string(a, ':', NULL)"
68
+ @db.literal(@a.join(':', '*')).should == "array_to_string(a, ':', '*')"
69
+ end
70
+
71
+ it "#unnest should use the unnest function" do
72
+ @db.literal(@a.unnest).should == "unnest(a)"
73
+ end
74
+
75
+ it "#pg_array should return self" do
76
+ @a.pg_array.should equal(@a)
77
+ end
78
+
79
+ it "Sequel.pg_array_op should return arg for ArrayOp" do
80
+ Sequel.pg_array_op(@a).should equal(@a)
81
+ end
82
+
83
+ it "should be able to turn expressions into array ops using pg_array" do
84
+ @db.literal(Sequel.qualify(:b, :a).pg_array.push(3)).should == "(b.a || 3)"
85
+ @db.literal(Sequel.function(:a, :b).pg_array.push(3)).should == "(a(b) || 3)"
86
+ end
87
+
88
+ it "should be able to turn literal strings into array ops using pg_array" do
89
+ @db.literal(Sequel.lit('a').pg_array.unnest).should == "unnest(a)"
90
+ end
91
+
92
+ it "should be able to turn symbols into array ops using Sequel.pg_array_op" do
93
+ @db.literal(Sequel.pg_array_op(:a).unnest).should == "unnest(a)"
94
+ end
95
+
96
+ it "should be able to turn symbols into array ops using Sequel.pg_array" do
97
+ @db.literal(Sequel.pg_array(:a).unnest).should == "unnest(a)"
98
+ end
99
+
100
+ it "should allow transforming PGArray instances into ArrayOp instances" do
101
+ @db.literal(Sequel.pg_array([1,2]).op.push(3)).should == "(ARRAY[1,2] || 3)"
102
+ end
103
+
104
+ it "should wrap array arguments in PGArrays" do
105
+ @db.literal(@a.contains([1, 2])).should == "(a @> ARRAY[1,2])"
106
+ @db.literal(@a.contained_by([1, 2])).should == "(a <@ ARRAY[1,2])"
107
+ @db.literal(@a.overlaps([1, 2])).should == "(a && ARRAY[1,2])"
108
+ @db.literal(@a.push([1, 2])).should == "(a || ARRAY[1,2])"
109
+ @db.literal(@a.concat([1, 2])).should == "(a || ARRAY[1,2])"
110
+ @db.literal(@a.unshift([1, 2])).should == "(ARRAY[1,2] || a)"
111
+ end
112
+ end
@@ -0,0 +1,324 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "pg_array extension" do
4
+ before(:all) do
5
+ @pg_types = Sequel::Postgres::PG_TYPES.dup
6
+ end
7
+ after(:all) do
8
+ Sequel::Postgres::PG_TYPES.replace(@pg_types)
9
+ end
10
+
11
+ before do
12
+ @db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
13
+ @db.extend_datasets(Module.new{def supports_timestamp_timezones?; false; end; def supports_timestamp_usecs?; false; end})
14
+ @db.extension(:pg_array)
15
+ @m = Sequel::Postgres
16
+ @converter = @m::PG_TYPES
17
+ end
18
+
19
+ it "should parse single dimensional text arrays" do
20
+ c = @converter[1009]
21
+ c.call("{a}").to_a.first.should be_a_kind_of(String)
22
+ c.call("{}").to_a.should == []
23
+ c.call("{a}").to_a.should == ['a']
24
+ c.call('{"a b"}').to_a.should == ['a b']
25
+ c.call('{a,b}').to_a.should == ['a', 'b']
26
+ end
27
+
28
+ it "should parse multi-dimensional text arrays" do
29
+ c = @converter[1009]
30
+ c.call("{{}}").to_a.should == [[]]
31
+ c.call("{{a},{b}}").to_a.should == [['a'], ['b']]
32
+ c.call('{{"a b"},{c}}').to_a.should == [['a b'], ['c']]
33
+ c.call('{{{a},{b}},{{c},{d}}}').to_a.should == [[['a'], ['b']], [['c'], ['d']]]
34
+ c.call('{{{a,e},{b,f}},{{c,g},{d,h}}}').to_a.should == [[['a', 'e'], ['b', 'f']], [['c', 'g'], ['d', 'h']]]
35
+ end
36
+
37
+ it "should parse text arrays with embedded deliminaters" do
38
+ c = @converter[1009]
39
+ c.call('{{"{},","\\",\\,\\\\\\"\\""}}').to_a.should == [['{},', '",,\\""']]
40
+ end
41
+
42
+ it "should parse single dimensional integer arrays" do
43
+ c = @converter[1007]
44
+ c.call("{1}").to_a.first.should be_a_kind_of(Integer)
45
+ c.call("{}").to_a.should == []
46
+ c.call("{1}").to_a.should == [1]
47
+ c.call('{2,3}').to_a.should == [2, 3]
48
+ c.call('{3,4,5}').to_a.should == [3, 4, 5]
49
+ end
50
+
51
+ it "should parse multiple dimensional integer arrays" do
52
+ c = @converter[1007]
53
+ c.call("{{}}").to_a.should == [[]]
54
+ c.call("{{1}}").to_a.should == [[1]]
55
+ c.call('{{2},{3}}').to_a.should == [[2], [3]]
56
+ c.call('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
57
+ end
58
+
59
+ it "should parse single dimensional float arrays" do
60
+ c = @converter[1022]
61
+ c.call("{}").to_a.should == []
62
+ c.call("{1.5}").to_a.should == [1.5]
63
+ c.call('{2.5,3.5}').to_a.should == [2.5, 3.5]
64
+ c.call('{3.5,4.5,5.5}').to_a.should == [3.5, 4.5, 5.5]
65
+ end
66
+
67
+ it "should parse multiple dimensional float arrays" do
68
+ c = @converter[1022]
69
+ c.call("{{}}").to_a.should == [[]]
70
+ c.call("{{1.5}}").to_a.should == [[1.5]]
71
+ c.call('{{2.5},{3.5}}').to_a.should == [[2.5], [3.5]]
72
+ c.call('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[1.5, 2.5], [3.5, 4.5]], [[5.5, 6.5], [7.5, 8.5]]]
73
+ end
74
+
75
+ it "should parse integers in float arrays as floats" do
76
+ c = @converter[1022]
77
+ c.call("{1}").to_a.first.should be_a_kind_of(Float)
78
+ c.call("{1}").to_a.should == [1.0]
79
+ c.call('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]
80
+ end
81
+
82
+ it "should parse single dimensional decimal arrays" do
83
+ c = @converter[1231]
84
+ c.call("{}").to_a.should == []
85
+ c.call("{1.5}").to_a.should == [BigDecimal.new('1.5')]
86
+ c.call('{2.5,3.5}').to_a.should == [BigDecimal.new('2.5'), BigDecimal.new('3.5')]
87
+ c.call('{3.5,4.5,5.5}').to_a.should == [BigDecimal.new('3.5'), BigDecimal.new('4.5'), BigDecimal.new('5.5')]
88
+ end
89
+
90
+ it "should parse multiple dimensional decimal arrays" do
91
+ c = @converter[1231]
92
+ c.call("{{}}").to_a.should == [[]]
93
+ c.call("{{1.5}}").to_a.should == [[BigDecimal.new('1.5')]]
94
+ c.call('{{2.5},{3.5}}').to_a.should == [[BigDecimal.new('2.5')], [BigDecimal.new('3.5')]]
95
+ c.call('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[BigDecimal.new('1.5'), BigDecimal.new('2.5')], [BigDecimal.new('3.5'), BigDecimal.new('4.5')]], [[BigDecimal.new('5.5'), BigDecimal.new('6.5')], [BigDecimal.new('7.5'), BigDecimal.new('8.5')]]]
96
+ end
97
+
98
+ it "should parse decimal values with arbitrary precision" do
99
+ c = @converter[1231]
100
+ c.call("{1.000000000000000000005}").to_a.should == [BigDecimal.new('1.000000000000000000005')]
101
+ c.call("{{1.000000000000000000005,2.000000000000000000005},{3.000000000000000000005,4.000000000000000000005}}").to_a.should == [[BigDecimal.new('1.000000000000000000005'), BigDecimal.new('2.000000000000000000005')], [BigDecimal.new('3.000000000000000000005'), BigDecimal.new('4.000000000000000000005')]]
102
+ end
103
+
104
+ it "should parse integers in decimal arrays as BigDecimals" do
105
+ c = @converter[1231]
106
+ c.call("{1}").to_a.first.should be_a_kind_of(BigDecimal)
107
+ c.call("{1}").to_a.should == [BigDecimal.new('1')]
108
+ c.call('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[BigDecimal.new('1'), BigDecimal.new('2')], [BigDecimal.new('3'), BigDecimal.new('4')]], [[BigDecimal.new('5'), BigDecimal.new('6')], [BigDecimal.new('7'), BigDecimal.new('8')]]]
109
+ end
110
+
111
+ it "should parse arrays with NULL values" do
112
+ @converter.values_at(1007, 1009, 1022, 1231).each do |c|
113
+ c.call("{NULL}").should == [nil]
114
+ c.call("{NULL,NULL}").should == [nil,nil]
115
+ c.call("{{NULL,NULL},{NULL,NULL}}").should == [[nil,nil],[nil,nil]]
116
+ end
117
+ end
118
+
119
+ it 'should parse arrays with "NULL" values' do
120
+ c = @converter[1009]
121
+ c.call('{NULL,"NULL",NULL}').to_a.should == [nil, "NULL", nil]
122
+ c.call('{NULLA,"NULL",NULL}').to_a.should == ["NULLA", "NULL", nil]
123
+ end
124
+
125
+ it "should literalize arrays without types correctly" do
126
+ @db.literal(@m::PGArray.new([])).should == 'ARRAY[]'
127
+ @db.literal(@m::PGArray.new([1])).should == 'ARRAY[1]'
128
+ @db.literal(@m::PGArray.new([nil])).should == 'ARRAY[NULL]'
129
+ @db.literal(@m::PGArray.new([nil, 1])).should == 'ARRAY[NULL,1]'
130
+ @db.literal(@m::PGArray.new([1.0, 2.5])).should == 'ARRAY[1.0,2.5]'
131
+ @db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')])).should == 'ARRAY[1.0,2.000000000000000000005]'
132
+ @db.literal(@m::PGArray.new([nil, "NULL"])).should == "ARRAY[NULL,'NULL']"
133
+ @db.literal(@m::PGArray.new([nil, "{},[]'\""])).should == "ARRAY[NULL,'{},[]''\"']"
134
+ end
135
+
136
+ it "should literalize multidimensional arrays correctly" do
137
+ @db.literal(@m::PGArray.new([[]])).should == 'ARRAY[[]]'
138
+ @db.literal(@m::PGArray.new([[1, 2]])).should == 'ARRAY[[1,2]]'
139
+ @db.literal(@m::PGArray.new([[3], [5]])).should == 'ARRAY[[3],[5]]'
140
+ @db.literal(@m::PGArray.new([[[1.0]], [[2.5]]])).should == 'ARRAY[[[1.0]],[[2.5]]]'
141
+ @db.literal(@m::PGArray.new([[[["NULL"]]]])).should == "ARRAY[[[['NULL']]]]"
142
+ @db.literal(@m::PGArray.new([["a", "b"], ["{},[]'\"", nil]])).should == "ARRAY[['a','b'],['{},[]''\"',NULL]]"
143
+ end
144
+
145
+ it "should literalize with types correctly" do
146
+ @db.literal(@m::PGArray.new([1], :int4)).should == 'ARRAY[1]::int4[]'
147
+ @db.literal(@m::PGArray.new([nil], :text)).should == 'ARRAY[NULL]::text[]'
148
+ @db.literal(@m::PGArray.new([nil, 1], :int8)).should == 'ARRAY[NULL,1]::int8[]'
149
+ @db.literal(@m::PGArray.new([1.0, 2.5], :real)).should == 'ARRAY[1.0,2.5]::real[]'
150
+ @db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')], :decimal)).should == 'ARRAY[1.0,2.000000000000000000005]::decimal[]'
151
+ @db.literal(@m::PGArray.new([nil, "NULL"], :varchar)).should == "ARRAY[NULL,'NULL']::varchar[]"
152
+ @db.literal(@m::PGArray.new([nil, "{},[]'\""], :"varchar(255)")).should == "ARRAY[NULL,'{},[]''\"']::varchar(255)[]"
153
+ end
154
+
155
+ it "should have Sequel.pg_array method for easy PGArray creation" do
156
+ @db.literal(Sequel.pg_array([1])).should == 'ARRAY[1]'
157
+ @db.literal(Sequel.pg_array([1, 2], :int4)).should == 'ARRAY[1,2]::int4[]'
158
+ @db.literal(Sequel.pg_array([[[1], [2]], [[3], [4]]], :real)).should == 'ARRAY[[[1],[2]],[[3],[4]]]::real[]'
159
+ end
160
+
161
+ it "should have Sequel.pg_array return existing PGArrays as-is" do
162
+ a = Sequel.pg_array([1])
163
+ Sequel.pg_array(a).should equal(a)
164
+ end
165
+
166
+ it "should have Sequel.pg_array create a new PGArrays if type of existing does not match" do
167
+ a = Sequel.pg_array([1], :int4)
168
+ b = Sequel.pg_array(a, :int8)
169
+ a.should == b
170
+ a.should_not equal(b)
171
+ a.array_type.should == :int4
172
+ b.array_type.should == :int8
173
+ end
174
+
175
+ it "should support using arrays as bound variables" do
176
+ @db.bound_variable_arg(1, nil).should == 1
177
+ @db.bound_variable_arg(Sequel.pg_array([1,2]), nil).should == '{1,2}'
178
+ @db.bound_variable_arg([1,2], nil).should == '{1,2}'
179
+ @db.bound_variable_arg([[1,2]], nil).should == '{{1,2}}'
180
+ @db.bound_variable_arg([1.0,2.0], nil).should == '{1.0,2.0}'
181
+ @db.bound_variable_arg([Sequel.lit('a'), Sequel.blob("a\0'\"")], nil).should == '{a,"a\\\\000\\\\047\\""}'
182
+ @db.bound_variable_arg(["\\ \"", 'NULL', nil], nil).should == '{"\\\\ \\"","NULL",NULL}'
183
+ end
184
+
185
+ it "should parse array types from the schema correctly" do
186
+ @db.fetch = [{:name=>'id', :db_type=>'integer'}, {:name=>'i', :db_type=>'integer[]'}, {:name=>'f', :db_type=>'real[]'}, {:name=>'d', :db_type=>'numeric[]'}, {:name=>'t', :db_type=>'text[]'}]
187
+ @db.schema(:items).map{|e| e[1][:type]}.should == [:integer, :integer_array, :float_array, :decimal_array, :string_array]
188
+ end
189
+
190
+ it "should support typecasting of the various array types" do
191
+ {
192
+ :integer=>{:class=>Integer, :convert=>['1', 1, '1']},
193
+ :float=>{:db_type=>'double precision', :class=>Float, :convert=>['1.1', 1.1, '1.1']},
194
+ :decimal=>{:db_type=>'numeric', :class=>BigDecimal, :convert=>['1.00000000000000000000000001', BigDecimal.new('1.00000000000000000000000001'), '1.00000000000000000000000001']},
195
+ :string=>{:db_type=>'text', :class=>String, :convert=>[1, '1', "'1'"]},
196
+ :bigint=>{:class=>Integer, :convert=>['1', 1, '1']},
197
+ :boolean=>{:class=>TrueClass, :convert=>['t', true, 'true']},
198
+ :blob=>{:db_type=>'bytea', :class=>Sequel::SQL::Blob, :convert=>['1', '1', "'1'"]},
199
+ :date=>{:class=>Date, :convert=>['2011-10-12', Date.new(2011, 10, 12), "'2011-10-12'"]},
200
+ :time=>{:db_type=>'time without time zone', :class=>Sequel::SQLTime, :convert=>['01:02:03', Sequel::SQLTime.create(1, 2, 3), "'01:02:03'"]},
201
+ :datetime=>{:db_type=>'timestamp without time zone', :class=>Time, :convert=>['2011-10-12 01:02:03', Time.local(2011, 10, 12, 1, 2, 3), "'2011-10-12 01:02:03'"]},
202
+ :time_timezone=>{:db_type=>'time with time zone', :class=>Sequel::SQLTime, :convert=>['01:02:03', Sequel::SQLTime.create(1, 2, 3), "'01:02:03'"]},
203
+ :datetime_timezone=>{:db_type=>'timestamp with time zone', :class=>Time, :convert=>['2011-10-12 01:02:03', Time.local(2011, 10, 12, 1, 2, 3), "'2011-10-12 01:02:03'"]},
204
+ }.each do |type, h|
205
+ meth = :"#{type}_array"
206
+ db_type = h[:db_type]||type
207
+ klass = h[:class]
208
+ array_in, value, output = h[:convert]
209
+
210
+ [[array_in]].each do |input|
211
+ v = @db.typecast_value(meth, input)
212
+ v.should == [value]
213
+ v.first.should be_a_kind_of(klass)
214
+ v.array_type.should_not be_nil
215
+ @db.typecast_value(meth, Sequel.pg_array([value])).should == v
216
+ @db.typecast_value(meth, v).should equal(v)
217
+ end
218
+
219
+ [[[array_in]]].each do |input|
220
+ v = @db.typecast_value(meth, input)
221
+ v.should == [[value]]
222
+ v.first.first.should be_a_kind_of(klass)
223
+ v.array_type.should_not be_nil
224
+ @db.typecast_value(meth, Sequel.pg_array([[value]])).should == v
225
+ @db.typecast_value(meth, v).should equal(v)
226
+ end
227
+
228
+ @db.literal(@db.typecast_value(meth, [array_in])).should == "ARRAY[#{output}]::#{db_type}[]"
229
+ @db.literal(@db.typecast_value(meth, [])).should == "ARRAY[]::#{db_type}[]"
230
+ end
231
+ proc{@db.typecast_value(:integer_array, {})}.should raise_error(Sequel::InvalidValue)
232
+ end
233
+
234
+ it "should support SQL::AliasMethods" do
235
+ @db.select(Sequel.pg_array([1], :integer).as(:col1)).sql.should == 'SELECT ARRAY[1]::integer[] AS col1'
236
+ end
237
+
238
+ it "should support registering custom array types" do
239
+ Sequel::Postgres::PGArray.register('foo')
240
+ @db.typecast_value(:foo_array, []).should be_a_kind_of(Sequel::Postgres::PGArray)
241
+ @db.fetch = [{:name=>'id', :db_type=>'foo[]'}]
242
+ @db.schema(:items).map{|e| e[1][:type]}.should == [:foo_array]
243
+ end
244
+
245
+ it "should support registering custom types with :type_symbol option" do
246
+ Sequel::Postgres::PGArray.register('foo', :type_symbol=>:bar)
247
+ @db.typecast_value(:bar_array, []).should be_a_kind_of(Sequel::Postgres::PGArray)
248
+ @db.fetch = [{:name=>'id', :db_type=>'foo[]'}]
249
+ @db.schema(:items).map{|e| e[1][:type]}.should == [:bar_array]
250
+ end
251
+
252
+ it "should support using a block as a custom conversion proc given as block" do
253
+ Sequel::Postgres::PGArray.register('foo', :oid=>1234){|s| (s*2).to_i}
254
+ @converter[1234].call('{1}').should == [11]
255
+ end
256
+
257
+ it "should support using a block as a custom conversion proc given as :converter option" do
258
+ Sequel::Postgres::PGArray.register('foo', :oid=>1234, :converter=>proc{|s| (s*2).to_i})
259
+ @converter[1234].call('{1}').should == [11]
260
+ end
261
+
262
+ it "should support using an existing scaler conversion proc via the :scalar_oid option" do
263
+ Sequel::Postgres::PGArray.register('foo', :oid=>1234, :scalar_oid=>16)
264
+ @converter[1234].call('{t}').should == [true]
265
+ end
266
+
267
+ it "should support using a given conversion procs hash via the :type_procs option" do
268
+ h = {16=>proc{|s| "!#{s}"}}
269
+ Sequel::Postgres::PGArray.register('foo', :oid=>1234, :scalar_oid=>16, :type_procs=>h)
270
+ h[1234].call('{t}').should == ["!t"]
271
+ end
272
+
273
+ it "should support adding methods to the given module via the :typecast_methods_module option" do
274
+ m = Module.new
275
+ Sequel::Postgres::PGArray.register('foo15', :scalar_typecast=>:boolean, :typecast_methods_module=>m)
276
+ @db.typecast_value(:foo15_array, ['t']).should == ['t']
277
+ @db.extend(m)
278
+ @db.typecast_value(:foo15_array, ['t']).should == [true]
279
+ end
280
+
281
+ it "should raise an error if using :scalar_oid option with unexisting scalar conversion proc" do
282
+ proc{Sequel::Postgres::PGArray.register('foo', :scalar_oid=>0)}.should raise_error(Sequel::Error)
283
+ end
284
+
285
+ it "should raise an error if using :converter option and a block argument" do
286
+ proc{Sequel::Postgres::PGArray.register('foo', :converter=>proc{}){}}.should raise_error(Sequel::Error)
287
+ end
288
+
289
+ it "should raise an error if using :scalar_oid option and a block argument" do
290
+ proc{Sequel::Postgres::PGArray.register('foo', :scalar_oid=>16){}}.should raise_error(Sequel::Error)
291
+ end
292
+
293
+ it "should support registering custom types with :oid option" do
294
+ Sequel::Postgres::PGArray.register('foo', :oid=>1)
295
+ Sequel::Postgres::PG_TYPES[1].call('{1}').should be_a_kind_of(Sequel::Postgres::PGArray)
296
+ end
297
+
298
+ it "should support registering custom types with :parser=>:json option" do
299
+ Sequel::Postgres::PGArray.register('foo', :oid=>2, :parser=>:json)
300
+ Sequel::Postgres::PG_TYPES[2].should be_a_kind_of(Sequel::Postgres::PGArray::JSONCreator)
301
+ end
302
+
303
+ it "should support registering converters with :parser=>:json option" do
304
+ Sequel::Postgres::PGArray.register('foo', :oid=>4, :parser=>:json){|s| s * 2}
305
+ Sequel::Postgres::PG_TYPES[4].call('{{1, 2}, {3, 4}}').should == [[2, 4], [6, 8]]
306
+ end
307
+
308
+ it "should support registering custom types with :array_type option" do
309
+ Sequel::Postgres::PGArray.register('foo', :oid=>3, :array_type=>:blah)
310
+ @db.literal(Sequel::Postgres::PG_TYPES[3].call('{}')).should == 'ARRAY[]::blah[]'
311
+ end
312
+
313
+ it "should use and not override existing database typecast method if :typecast_method option is given" do
314
+ Sequel::Postgres::PGArray.register('foo', :typecast_method=>:float)
315
+ @db.fetch = [{:name=>'id', :db_type=>'foo[]'}]
316
+ @db.schema(:items).map{|e| e[1][:type]}.should == [:float_array]
317
+ end
318
+
319
+ it "should set appropriate timestamp conversion procs when getting conversion procs" do
320
+ procs = @db.conversion_procs
321
+ procs[1185].call('{"2011-10-20 11:12:13"}').should == [Time.local(2011, 10, 20, 11, 12, 13)]
322
+ procs[1115].call('{"2011-10-20 11:12:13"}').should == [Time.local(2011, 10, 20, 11, 12, 13)]
323
+ end
324
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "pg_auto_parameterize extension" do
4
+ before do
5
+ @db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
6
+ @db.synchronize{|c| def c.escape_bytea(v) v*2 end}
7
+ @db.extend_datasets{def use_cursor(*) clone end}
8
+ @db.extension :pg_auto_parameterize
9
+ end
10
+
11
+ it "should automatically parameterize queries strings, blobs, numerics, dates, and times" do
12
+ pr = proc do |ds, sql, *args|
13
+ arg = args[0]
14
+ parg = args[1] || arg
15
+ s = ds.filter(:a=>arg).sql
16
+ s.should == sql
17
+ s.args.should == (parg == :nil ? nil : [parg])
18
+ end
19
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::int4)', 1)
20
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::int8)', 18446744073709551616)
21
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::double precision)', 1.1)
22
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::numeric)', BigDecimal.new('1.01'))
23
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1)', "a")
24
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::bytea)', Sequel.blob("a\0b"))
25
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = 1)', Sequel.lit('1'), :nil)
26
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::time)', Sequel::SQLTime.create(1, 2, 3, 500000))
27
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::date)', Date.today)
28
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::timestamp)', DateTime.new(2012, 1, 2, 3, 4, 5))
29
+ pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::timestamp)', Time.utc(2012, 1, 2, 3, 4, 5))
30
+ end
31
+
32
+ it "should extract parameters from query string when executing" do
33
+ @db.fetch = {:a=>1}
34
+ @db.numrows = 1
35
+ @db.autoid = 1
36
+
37
+ @db[:table].filter(:a=>1).all.should == [{:a=>1}]
38
+ @db.sqls.should == ['SELECT * FROM table WHERE (a = $1::int4) -- args: [1]']
39
+
40
+ @db[:table].filter(:a=>1).update(:b=>'a').should == 1
41
+ @db.sqls.should == ['UPDATE table SET b = $1 WHERE (a = $2::int4) -- args: ["a", 1]']
42
+
43
+ @db[:table].filter(:a=>1).delete.should == 1
44
+ @db.sqls.should == ['DELETE FROM table WHERE (a = $1::int4) -- args: [1]']
45
+
46
+ @db[:table].insert(:a=>1).should == 1
47
+ @db.sqls.should == ['INSERT INTO table (a) VALUES ($1::int4) RETURNING id -- args: [1]']
48
+ end
49
+
50
+ it "should not automatically paramiterize if no_auto_parameterize is used" do
51
+ @db[:table].no_auto_parameterize.filter(:a=>1).sql.should == 'SELECT * FROM table WHERE (a = 1)'
52
+ end
53
+
54
+ it "should not automatically parameterize prepared statements" do
55
+ @db[:table].filter(:a=>1, :b=>:$b).prepare(:select).sql.should =~ /SELECT \* FROM table WHERE \(\((a = 1|b = \$b)\) AND \((a = 1|b = \$b)\)\)/
56
+ end
57
+
58
+ it "should show args with string when inspecting SQL " do
59
+ @db[:table].filter(:a=>1).sql.inspect.should == '"SELECT * FROM table WHERE (a = $1::int4); [1]"'
60
+ end
61
+
62
+ it "should not auto parameterize when using cursors" do
63
+ @db[:table].filter(:a=>1).use_cursor.opts[:no_auto_parameterize].should be_true
64
+ end
65
+ end