sequel 4.36.0 → 5.61.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 (760) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG +548 -5749
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +265 -159
  5. data/bin/sequel +34 -12
  6. data/doc/advanced_associations.rdoc +228 -187
  7. data/doc/association_basics.rdoc +281 -291
  8. data/doc/bin_sequel.rdoc +5 -3
  9. data/doc/cheat_sheet.rdoc +86 -51
  10. data/doc/code_order.rdoc +25 -19
  11. data/doc/core_extensions.rdoc +104 -63
  12. data/doc/dataset_basics.rdoc +12 -21
  13. data/doc/dataset_filtering.rdoc +99 -86
  14. data/doc/extensions.rdoc +3 -10
  15. data/doc/fork_safety.rdoc +84 -0
  16. data/doc/mass_assignment.rdoc +74 -31
  17. data/doc/migration.rdoc +59 -51
  18. data/doc/model_dataset_method_design.rdoc +129 -0
  19. data/doc/model_hooks.rdoc +15 -25
  20. data/doc/model_plugins.rdoc +12 -12
  21. data/doc/mssql_stored_procedures.rdoc +3 -3
  22. data/doc/object_model.rdoc +58 -68
  23. data/doc/opening_databases.rdoc +85 -95
  24. data/doc/postgresql.rdoc +263 -38
  25. data/doc/prepared_statements.rdoc +29 -24
  26. data/doc/querying.rdoc +189 -167
  27. data/doc/reflection.rdoc +5 -6
  28. data/doc/release_notes/5.0.0.txt +159 -0
  29. data/doc/release_notes/5.1.0.txt +31 -0
  30. data/doc/release_notes/5.10.0.txt +84 -0
  31. data/doc/release_notes/5.11.0.txt +83 -0
  32. data/doc/release_notes/5.12.0.txt +141 -0
  33. data/doc/release_notes/5.13.0.txt +27 -0
  34. data/doc/release_notes/5.14.0.txt +63 -0
  35. data/doc/release_notes/5.15.0.txt +39 -0
  36. data/doc/release_notes/5.16.0.txt +110 -0
  37. data/doc/release_notes/5.17.0.txt +31 -0
  38. data/doc/release_notes/5.18.0.txt +69 -0
  39. data/doc/release_notes/5.19.0.txt +28 -0
  40. data/doc/release_notes/5.2.0.txt +33 -0
  41. data/doc/release_notes/5.20.0.txt +89 -0
  42. data/doc/release_notes/5.21.0.txt +87 -0
  43. data/doc/release_notes/5.22.0.txt +48 -0
  44. data/doc/release_notes/5.23.0.txt +56 -0
  45. data/doc/release_notes/5.24.0.txt +56 -0
  46. data/doc/release_notes/5.25.0.txt +32 -0
  47. data/doc/release_notes/5.26.0.txt +35 -0
  48. data/doc/release_notes/5.27.0.txt +21 -0
  49. data/doc/release_notes/5.28.0.txt +16 -0
  50. data/doc/release_notes/5.29.0.txt +22 -0
  51. data/doc/release_notes/5.3.0.txt +121 -0
  52. data/doc/release_notes/5.30.0.txt +20 -0
  53. data/doc/release_notes/5.31.0.txt +148 -0
  54. data/doc/release_notes/5.32.0.txt +46 -0
  55. data/doc/release_notes/5.33.0.txt +24 -0
  56. data/doc/release_notes/5.34.0.txt +40 -0
  57. data/doc/release_notes/5.35.0.txt +56 -0
  58. data/doc/release_notes/5.36.0.txt +60 -0
  59. data/doc/release_notes/5.37.0.txt +30 -0
  60. data/doc/release_notes/5.38.0.txt +28 -0
  61. data/doc/release_notes/5.39.0.txt +19 -0
  62. data/doc/release_notes/5.4.0.txt +80 -0
  63. data/doc/release_notes/5.40.0.txt +40 -0
  64. data/doc/release_notes/5.41.0.txt +25 -0
  65. data/doc/release_notes/5.42.0.txt +136 -0
  66. data/doc/release_notes/5.43.0.txt +98 -0
  67. data/doc/release_notes/5.44.0.txt +32 -0
  68. data/doc/release_notes/5.45.0.txt +34 -0
  69. data/doc/release_notes/5.46.0.txt +87 -0
  70. data/doc/release_notes/5.47.0.txt +59 -0
  71. data/doc/release_notes/5.48.0.txt +14 -0
  72. data/doc/release_notes/5.49.0.txt +59 -0
  73. data/doc/release_notes/5.5.0.txt +61 -0
  74. data/doc/release_notes/5.50.0.txt +78 -0
  75. data/doc/release_notes/5.51.0.txt +47 -0
  76. data/doc/release_notes/5.52.0.txt +87 -0
  77. data/doc/release_notes/5.53.0.txt +23 -0
  78. data/doc/release_notes/5.54.0.txt +27 -0
  79. data/doc/release_notes/5.55.0.txt +21 -0
  80. data/doc/release_notes/5.56.0.txt +51 -0
  81. data/doc/release_notes/5.57.0.txt +23 -0
  82. data/doc/release_notes/5.58.0.txt +31 -0
  83. data/doc/release_notes/5.59.0.txt +73 -0
  84. data/doc/release_notes/5.6.0.txt +31 -0
  85. data/doc/release_notes/5.60.0.txt +22 -0
  86. data/doc/release_notes/5.61.0.txt +43 -0
  87. data/doc/release_notes/5.7.0.txt +108 -0
  88. data/doc/release_notes/5.8.0.txt +170 -0
  89. data/doc/release_notes/5.9.0.txt +99 -0
  90. data/doc/schema_modification.rdoc +95 -75
  91. data/doc/security.rdoc +109 -80
  92. data/doc/sharding.rdoc +74 -47
  93. data/doc/sql.rdoc +147 -122
  94. data/doc/testing.rdoc +43 -20
  95. data/doc/thread_safety.rdoc +2 -4
  96. data/doc/transactions.rdoc +97 -18
  97. data/doc/validations.rdoc +52 -50
  98. data/doc/virtual_rows.rdoc +90 -109
  99. data/lib/sequel/adapters/ado/access.rb +15 -17
  100. data/lib/sequel/adapters/ado/mssql.rb +6 -15
  101. data/lib/sequel/adapters/ado.rb +150 -20
  102. data/lib/sequel/adapters/amalgalite.rb +11 -23
  103. data/lib/sequel/adapters/ibmdb.rb +47 -55
  104. data/lib/sequel/adapters/jdbc/db2.rb +29 -39
  105. data/lib/sequel/adapters/jdbc/derby.rb +58 -54
  106. data/lib/sequel/adapters/jdbc/h2.rb +93 -35
  107. data/lib/sequel/adapters/jdbc/hsqldb.rb +24 -31
  108. data/lib/sequel/adapters/jdbc/jtds.rb +2 -10
  109. data/lib/sequel/adapters/jdbc/mssql.rb +3 -11
  110. data/lib/sequel/adapters/jdbc/mysql.rb +17 -20
  111. data/lib/sequel/adapters/jdbc/oracle.rb +22 -18
  112. data/lib/sequel/adapters/jdbc/postgresql.rb +69 -71
  113. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +11 -23
  114. data/lib/sequel/adapters/jdbc/sqlite.rb +47 -11
  115. data/lib/sequel/adapters/jdbc/sqlserver.rb +34 -9
  116. data/lib/sequel/adapters/jdbc/transactions.rb +22 -38
  117. data/lib/sequel/adapters/jdbc.rb +145 -130
  118. data/lib/sequel/adapters/mock.rb +100 -111
  119. data/lib/sequel/adapters/mysql.rb +114 -122
  120. data/lib/sequel/adapters/mysql2.rb +147 -63
  121. data/lib/sequel/adapters/odbc/db2.rb +1 -1
  122. data/lib/sequel/adapters/odbc/mssql.rb +8 -14
  123. data/lib/sequel/adapters/odbc/oracle.rb +11 -0
  124. data/lib/sequel/adapters/odbc.rb +20 -25
  125. data/lib/sequel/adapters/oracle.rb +50 -56
  126. data/lib/sequel/adapters/postgres.rb +305 -327
  127. data/lib/sequel/adapters/postgresql.rb +1 -1
  128. data/lib/sequel/adapters/shared/access.rb +74 -78
  129. data/lib/sequel/adapters/shared/db2.rb +118 -71
  130. data/lib/sequel/adapters/shared/mssql.rb +301 -220
  131. data/lib/sequel/adapters/shared/mysql.rb +299 -217
  132. data/lib/sequel/adapters/shared/oracle.rb +226 -65
  133. data/lib/sequel/adapters/shared/postgres.rb +935 -395
  134. data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -126
  135. data/lib/sequel/adapters/shared/sqlite.rb +447 -173
  136. data/lib/sequel/adapters/sqlanywhere.rb +48 -35
  137. data/lib/sequel/adapters/sqlite.rb +156 -111
  138. data/lib/sequel/adapters/tinytds.rb +30 -38
  139. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  140. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +3 -6
  141. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -2
  142. data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
  143. data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
  144. data/lib/sequel/adapters/utils/replace.rb +1 -4
  145. data/lib/sequel/adapters/utils/stored_procedures.rb +7 -22
  146. data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
  147. data/lib/sequel/ast_transformer.rb +17 -89
  148. data/lib/sequel/connection_pool/sharded_single.rb +18 -15
  149. data/lib/sequel/connection_pool/sharded_threaded.rb +130 -111
  150. data/lib/sequel/connection_pool/single.rb +18 -13
  151. data/lib/sequel/connection_pool/threaded.rb +121 -120
  152. data/lib/sequel/connection_pool.rb +48 -29
  153. data/lib/sequel/core.rb +351 -301
  154. data/lib/sequel/database/connecting.rb +69 -57
  155. data/lib/sequel/database/dataset.rb +13 -5
  156. data/lib/sequel/database/dataset_defaults.rb +18 -102
  157. data/lib/sequel/database/features.rb +18 -4
  158. data/lib/sequel/database/logging.rb +12 -11
  159. data/lib/sequel/database/misc.rb +180 -122
  160. data/lib/sequel/database/query.rb +47 -27
  161. data/lib/sequel/database/schema_generator.rb +178 -84
  162. data/lib/sequel/database/schema_methods.rb +172 -97
  163. data/lib/sequel/database/transactions.rb +205 -44
  164. data/lib/sequel/database.rb +17 -2
  165. data/lib/sequel/dataset/actions.rb +339 -155
  166. data/lib/sequel/dataset/dataset_module.rb +46 -0
  167. data/lib/sequel/dataset/features.rb +90 -35
  168. data/lib/sequel/dataset/graph.rb +80 -58
  169. data/lib/sequel/dataset/misc.rb +137 -47
  170. data/lib/sequel/dataset/placeholder_literalizer.rb +63 -25
  171. data/lib/sequel/dataset/prepared_statements.rb +188 -85
  172. data/lib/sequel/dataset/query.rb +530 -222
  173. data/lib/sequel/dataset/sql.rb +590 -368
  174. data/lib/sequel/dataset.rb +26 -16
  175. data/lib/sequel/deprecated.rb +12 -2
  176. data/lib/sequel/exceptions.rb +46 -16
  177. data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
  178. data/lib/sequel/extensions/_model_pg_row.rb +43 -0
  179. data/lib/sequel/extensions/_pretty_table.rb +2 -5
  180. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  181. data/lib/sequel/extensions/arbitrary_servers.rb +10 -10
  182. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  183. data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
  184. data/lib/sequel/extensions/blank.rb +8 -0
  185. data/lib/sequel/extensions/caller_logging.rb +79 -0
  186. data/lib/sequel/extensions/columns_introspection.rb +4 -3
  187. data/lib/sequel/extensions/connection_expiration.rb +20 -10
  188. data/lib/sequel/extensions/connection_validator.rb +11 -10
  189. data/lib/sequel/extensions/constant_sql_override.rb +65 -0
  190. data/lib/sequel/extensions/constraint_validations.rb +62 -39
  191. data/lib/sequel/extensions/core_extensions.rb +42 -48
  192. data/lib/sequel/extensions/core_refinements.rb +80 -59
  193. data/lib/sequel/extensions/current_datetime_timestamp.rb +1 -4
  194. data/lib/sequel/extensions/date_arithmetic.rb +98 -39
  195. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  196. data/lib/sequel/extensions/datetime_parse_to_time.rb +41 -0
  197. data/lib/sequel/extensions/duplicate_columns_handler.rb +21 -14
  198. data/lib/sequel/extensions/empty_array_consider_nulls.rb +2 -2
  199. data/lib/sequel/extensions/escaped_like.rb +100 -0
  200. data/lib/sequel/extensions/eval_inspect.rb +12 -15
  201. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  202. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  203. data/lib/sequel/extensions/freeze_datasets.rb +3 -0
  204. data/lib/sequel/extensions/from_block.rb +1 -34
  205. data/lib/sequel/extensions/graph_each.rb +4 -4
  206. data/lib/sequel/extensions/identifier_mangling.rb +180 -0
  207. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  208. data/lib/sequel/extensions/index_caching.rb +109 -0
  209. data/lib/sequel/extensions/inflector.rb +13 -5
  210. data/lib/sequel/extensions/integer64.rb +32 -0
  211. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  212. data/lib/sequel/extensions/looser_typecasting.rb +17 -8
  213. data/lib/sequel/extensions/migration.rb +119 -78
  214. data/lib/sequel/extensions/named_timezones.rb +88 -23
  215. data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -82
  216. data/lib/sequel/extensions/null_dataset.rb +8 -8
  217. data/lib/sequel/extensions/pagination.rb +32 -29
  218. data/lib/sequel/extensions/pg_array.rb +221 -287
  219. data/lib/sequel/extensions/pg_array_ops.rb +17 -9
  220. data/lib/sequel/extensions/pg_enum.rb +63 -23
  221. data/lib/sequel/extensions/pg_extended_date_support.rb +241 -0
  222. data/lib/sequel/extensions/pg_hstore.rb +45 -54
  223. data/lib/sequel/extensions/pg_hstore_ops.rb +58 -6
  224. data/lib/sequel/extensions/pg_inet.rb +31 -12
  225. data/lib/sequel/extensions/pg_inet_ops.rb +2 -2
  226. data/lib/sequel/extensions/pg_interval.rb +56 -29
  227. data/lib/sequel/extensions/pg_json.rb +417 -140
  228. data/lib/sequel/extensions/pg_json_ops.rb +270 -18
  229. data/lib/sequel/extensions/pg_loose_count.rb +4 -2
  230. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  231. data/lib/sequel/extensions/pg_range.rb +131 -191
  232. data/lib/sequel/extensions/pg_range_ops.rb +42 -13
  233. data/lib/sequel/extensions/pg_row.rb +48 -81
  234. data/lib/sequel/extensions/pg_row_ops.rb +33 -14
  235. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
  236. data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
  237. data/lib/sequel/extensions/query.rb +9 -7
  238. data/lib/sequel/extensions/round_timestamps.rb +0 -6
  239. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  240. data/lib/sequel/extensions/s.rb +60 -0
  241. data/lib/sequel/extensions/schema_caching.rb +10 -1
  242. data/lib/sequel/extensions/schema_dumper.rb +71 -48
  243. data/lib/sequel/extensions/select_remove.rb +4 -4
  244. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
  245. data/lib/sequel/extensions/server_block.rb +51 -27
  246. data/lib/sequel/extensions/split_array_nil.rb +4 -4
  247. data/lib/sequel/extensions/sql_comments.rb +119 -7
  248. data/lib/sequel/extensions/sql_expr.rb +2 -1
  249. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  250. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  251. data/lib/sequel/extensions/string_agg.rb +11 -8
  252. data/lib/sequel/extensions/string_date_time.rb +19 -23
  253. data/lib/sequel/extensions/symbol_aref.rb +55 -0
  254. data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
  255. data/lib/sequel/extensions/symbol_as.rb +23 -0
  256. data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
  257. data/lib/sequel/extensions/synchronize_sql.rb +45 -0
  258. data/lib/sequel/extensions/to_dot.rb +10 -4
  259. data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
  260. data/lib/sequel/model/associations.rb +1006 -284
  261. data/lib/sequel/model/base.rb +560 -805
  262. data/lib/sequel/model/dataset_module.rb +11 -10
  263. data/lib/sequel/model/default_inflections.rb +1 -1
  264. data/lib/sequel/model/errors.rb +10 -3
  265. data/lib/sequel/model/exceptions.rb +8 -10
  266. data/lib/sequel/model/inflections.rb +7 -20
  267. data/lib/sequel/model/plugins.rb +114 -0
  268. data/lib/sequel/model.rb +32 -82
  269. data/lib/sequel/plugins/active_model.rb +30 -14
  270. data/lib/sequel/plugins/after_initialize.rb +1 -1
  271. data/lib/sequel/plugins/association_dependencies.rb +25 -18
  272. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  273. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  274. data/lib/sequel/plugins/association_pks.rb +147 -70
  275. data/lib/sequel/plugins/association_proxies.rb +33 -9
  276. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  277. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  278. data/lib/sequel/plugins/auto_validations.rb +95 -28
  279. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  280. data/lib/sequel/plugins/before_after_save.rb +0 -42
  281. data/lib/sequel/plugins/blacklist_security.rb +21 -12
  282. data/lib/sequel/plugins/boolean_readers.rb +5 -5
  283. data/lib/sequel/plugins/boolean_subsets.rb +13 -8
  284. data/lib/sequel/plugins/caching.rb +25 -16
  285. data/lib/sequel/plugins/class_table_inheritance.rb +179 -100
  286. data/lib/sequel/plugins/column_conflicts.rb +16 -3
  287. data/lib/sequel/plugins/column_encryption.rb +728 -0
  288. data/lib/sequel/plugins/column_select.rb +7 -5
  289. data/lib/sequel/plugins/columns_updated.rb +42 -0
  290. data/lib/sequel/plugins/composition.rb +42 -26
  291. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  292. data/lib/sequel/plugins/constraint_validations.rb +20 -14
  293. data/lib/sequel/plugins/csv_serializer.rb +56 -35
  294. data/lib/sequel/plugins/dataset_associations.rb +40 -17
  295. data/lib/sequel/plugins/def_dataset_method.rb +90 -0
  296. data/lib/sequel/plugins/defaults_setter.rb +65 -10
  297. data/lib/sequel/plugins/delay_add_association.rb +1 -1
  298. data/lib/sequel/plugins/dirty.rb +62 -24
  299. data/lib/sequel/plugins/eager_each.rb +3 -3
  300. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  301. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  302. data/lib/sequel/plugins/enum.rb +124 -0
  303. data/lib/sequel/plugins/error_splitter.rb +17 -12
  304. data/lib/sequel/plugins/finder.rb +246 -0
  305. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  306. data/lib/sequel/plugins/force_encoding.rb +7 -12
  307. data/lib/sequel/plugins/hook_class_methods.rb +37 -54
  308. data/lib/sequel/plugins/input_transformer.rb +18 -10
  309. data/lib/sequel/plugins/insert_conflict.rb +76 -0
  310. data/lib/sequel/plugins/insert_returning_select.rb +2 -2
  311. data/lib/sequel/plugins/instance_filters.rb +10 -8
  312. data/lib/sequel/plugins/instance_hooks.rb +34 -17
  313. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  314. data/lib/sequel/plugins/inverted_subsets.rb +22 -13
  315. data/lib/sequel/plugins/json_serializer.rb +124 -64
  316. data/lib/sequel/plugins/lazy_attributes.rb +21 -14
  317. data/lib/sequel/plugins/list.rb +35 -21
  318. data/lib/sequel/plugins/many_through_many.rb +134 -21
  319. data/lib/sequel/plugins/modification_detection.rb +15 -5
  320. data/lib/sequel/plugins/mssql_optimistic_locking.rb +6 -5
  321. data/lib/sequel/plugins/nested_attributes.rb +61 -31
  322. data/lib/sequel/plugins/optimistic_locking.rb +3 -3
  323. data/lib/sequel/plugins/pg_array_associations.rb +103 -53
  324. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +350 -0
  325. data/lib/sequel/plugins/pg_row.rb +5 -51
  326. data/lib/sequel/plugins/prepared_statements.rb +60 -72
  327. data/lib/sequel/plugins/prepared_statements_safe.rb +9 -4
  328. data/lib/sequel/plugins/rcte_tree.rb +68 -82
  329. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  330. data/lib/sequel/plugins/serialization.rb +43 -46
  331. data/lib/sequel/plugins/serialization_modification_detection.rb +3 -2
  332. data/lib/sequel/plugins/sharding.rb +15 -10
  333. data/lib/sequel/plugins/single_table_inheritance.rb +67 -28
  334. data/lib/sequel/plugins/skip_create_refresh.rb +3 -3
  335. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  336. data/lib/sequel/plugins/split_values.rb +11 -6
  337. data/lib/sequel/plugins/sql_comments.rb +189 -0
  338. data/lib/sequel/plugins/static_cache.rb +77 -53
  339. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  340. data/lib/sequel/plugins/string_stripper.rb +3 -3
  341. data/lib/sequel/plugins/subclasses.rb +43 -10
  342. data/lib/sequel/plugins/subset_conditions.rb +15 -5
  343. data/lib/sequel/plugins/table_select.rb +2 -2
  344. data/lib/sequel/plugins/tactical_eager_loading.rb +96 -12
  345. data/lib/sequel/plugins/throw_failures.rb +110 -0
  346. data/lib/sequel/plugins/timestamps.rb +20 -8
  347. data/lib/sequel/plugins/touch.rb +19 -8
  348. data/lib/sequel/plugins/tree.rb +62 -32
  349. data/lib/sequel/plugins/typecast_on_load.rb +12 -4
  350. data/lib/sequel/plugins/unlimited_update.rb +1 -7
  351. data/lib/sequel/plugins/unused_associations.rb +521 -0
  352. data/lib/sequel/plugins/update_or_create.rb +4 -4
  353. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  354. data/lib/sequel/plugins/update_refresh.rb +26 -15
  355. data/lib/sequel/plugins/uuid.rb +7 -11
  356. data/lib/sequel/plugins/validate_associated.rb +18 -0
  357. data/lib/sequel/plugins/validation_class_methods.rb +38 -19
  358. data/lib/sequel/plugins/validation_contexts.rb +49 -0
  359. data/lib/sequel/plugins/validation_helpers.rb +57 -41
  360. data/lib/sequel/plugins/whitelist_security.rb +122 -0
  361. data/lib/sequel/plugins/xml_serializer.rb +30 -31
  362. data/lib/sequel/sql.rb +471 -331
  363. data/lib/sequel/timezones.rb +78 -47
  364. data/lib/sequel/version.rb +7 -2
  365. data/lib/sequel.rb +1 -1
  366. metadata +217 -521
  367. data/Rakefile +0 -164
  368. data/doc/active_record.rdoc +0 -928
  369. data/doc/release_notes/1.0.txt +0 -38
  370. data/doc/release_notes/1.1.txt +0 -143
  371. data/doc/release_notes/1.3.txt +0 -101
  372. data/doc/release_notes/1.4.0.txt +0 -53
  373. data/doc/release_notes/1.5.0.txt +0 -155
  374. data/doc/release_notes/2.0.0.txt +0 -298
  375. data/doc/release_notes/2.1.0.txt +0 -271
  376. data/doc/release_notes/2.10.0.txt +0 -328
  377. data/doc/release_notes/2.11.0.txt +0 -215
  378. data/doc/release_notes/2.12.0.txt +0 -534
  379. data/doc/release_notes/2.2.0.txt +0 -253
  380. data/doc/release_notes/2.3.0.txt +0 -88
  381. data/doc/release_notes/2.4.0.txt +0 -106
  382. data/doc/release_notes/2.5.0.txt +0 -137
  383. data/doc/release_notes/2.6.0.txt +0 -157
  384. data/doc/release_notes/2.7.0.txt +0 -166
  385. data/doc/release_notes/2.8.0.txt +0 -171
  386. data/doc/release_notes/2.9.0.txt +0 -97
  387. data/doc/release_notes/3.0.0.txt +0 -221
  388. data/doc/release_notes/3.1.0.txt +0 -406
  389. data/doc/release_notes/3.10.0.txt +0 -286
  390. data/doc/release_notes/3.11.0.txt +0 -254
  391. data/doc/release_notes/3.12.0.txt +0 -304
  392. data/doc/release_notes/3.13.0.txt +0 -210
  393. data/doc/release_notes/3.14.0.txt +0 -118
  394. data/doc/release_notes/3.15.0.txt +0 -78
  395. data/doc/release_notes/3.16.0.txt +0 -45
  396. data/doc/release_notes/3.17.0.txt +0 -58
  397. data/doc/release_notes/3.18.0.txt +0 -120
  398. data/doc/release_notes/3.19.0.txt +0 -67
  399. data/doc/release_notes/3.2.0.txt +0 -268
  400. data/doc/release_notes/3.20.0.txt +0 -41
  401. data/doc/release_notes/3.21.0.txt +0 -87
  402. data/doc/release_notes/3.22.0.txt +0 -39
  403. data/doc/release_notes/3.23.0.txt +0 -172
  404. data/doc/release_notes/3.24.0.txt +0 -420
  405. data/doc/release_notes/3.25.0.txt +0 -88
  406. data/doc/release_notes/3.26.0.txt +0 -88
  407. data/doc/release_notes/3.27.0.txt +0 -82
  408. data/doc/release_notes/3.28.0.txt +0 -304
  409. data/doc/release_notes/3.29.0.txt +0 -459
  410. data/doc/release_notes/3.3.0.txt +0 -192
  411. data/doc/release_notes/3.30.0.txt +0 -135
  412. data/doc/release_notes/3.31.0.txt +0 -146
  413. data/doc/release_notes/3.32.0.txt +0 -202
  414. data/doc/release_notes/3.33.0.txt +0 -157
  415. data/doc/release_notes/3.34.0.txt +0 -671
  416. data/doc/release_notes/3.35.0.txt +0 -144
  417. data/doc/release_notes/3.36.0.txt +0 -245
  418. data/doc/release_notes/3.37.0.txt +0 -338
  419. data/doc/release_notes/3.38.0.txt +0 -234
  420. data/doc/release_notes/3.39.0.txt +0 -237
  421. data/doc/release_notes/3.4.0.txt +0 -325
  422. data/doc/release_notes/3.40.0.txt +0 -73
  423. data/doc/release_notes/3.41.0.txt +0 -155
  424. data/doc/release_notes/3.42.0.txt +0 -74
  425. data/doc/release_notes/3.43.0.txt +0 -105
  426. data/doc/release_notes/3.44.0.txt +0 -152
  427. data/doc/release_notes/3.45.0.txt +0 -179
  428. data/doc/release_notes/3.46.0.txt +0 -122
  429. data/doc/release_notes/3.47.0.txt +0 -270
  430. data/doc/release_notes/3.48.0.txt +0 -477
  431. data/doc/release_notes/3.5.0.txt +0 -510
  432. data/doc/release_notes/3.6.0.txt +0 -366
  433. data/doc/release_notes/3.7.0.txt +0 -179
  434. data/doc/release_notes/3.8.0.txt +0 -151
  435. data/doc/release_notes/3.9.0.txt +0 -233
  436. data/doc/release_notes/4.0.0.txt +0 -262
  437. data/doc/release_notes/4.1.0.txt +0 -85
  438. data/doc/release_notes/4.10.0.txt +0 -226
  439. data/doc/release_notes/4.11.0.txt +0 -147
  440. data/doc/release_notes/4.12.0.txt +0 -105
  441. data/doc/release_notes/4.13.0.txt +0 -169
  442. data/doc/release_notes/4.14.0.txt +0 -68
  443. data/doc/release_notes/4.15.0.txt +0 -56
  444. data/doc/release_notes/4.16.0.txt +0 -36
  445. data/doc/release_notes/4.17.0.txt +0 -38
  446. data/doc/release_notes/4.18.0.txt +0 -36
  447. data/doc/release_notes/4.19.0.txt +0 -45
  448. data/doc/release_notes/4.2.0.txt +0 -129
  449. data/doc/release_notes/4.20.0.txt +0 -79
  450. data/doc/release_notes/4.21.0.txt +0 -94
  451. data/doc/release_notes/4.22.0.txt +0 -72
  452. data/doc/release_notes/4.23.0.txt +0 -65
  453. data/doc/release_notes/4.24.0.txt +0 -99
  454. data/doc/release_notes/4.25.0.txt +0 -181
  455. data/doc/release_notes/4.26.0.txt +0 -44
  456. data/doc/release_notes/4.27.0.txt +0 -78
  457. data/doc/release_notes/4.28.0.txt +0 -57
  458. data/doc/release_notes/4.29.0.txt +0 -41
  459. data/doc/release_notes/4.3.0.txt +0 -40
  460. data/doc/release_notes/4.30.0.txt +0 -37
  461. data/doc/release_notes/4.31.0.txt +0 -57
  462. data/doc/release_notes/4.32.0.txt +0 -132
  463. data/doc/release_notes/4.33.0.txt +0 -88
  464. data/doc/release_notes/4.34.0.txt +0 -86
  465. data/doc/release_notes/4.35.0.txt +0 -130
  466. data/doc/release_notes/4.36.0.txt +0 -116
  467. data/doc/release_notes/4.4.0.txt +0 -92
  468. data/doc/release_notes/4.5.0.txt +0 -34
  469. data/doc/release_notes/4.6.0.txt +0 -30
  470. data/doc/release_notes/4.7.0.txt +0 -103
  471. data/doc/release_notes/4.8.0.txt +0 -175
  472. data/doc/release_notes/4.9.0.txt +0 -190
  473. data/lib/sequel/adapters/cubrid.rb +0 -144
  474. data/lib/sequel/adapters/do/mysql.rb +0 -66
  475. data/lib/sequel/adapters/do/postgres.rb +0 -44
  476. data/lib/sequel/adapters/do/sqlite3.rb +0 -42
  477. data/lib/sequel/adapters/do.rb +0 -158
  478. data/lib/sequel/adapters/jdbc/as400.rb +0 -84
  479. data/lib/sequel/adapters/jdbc/cubrid.rb +0 -64
  480. data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -36
  481. data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -33
  482. data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -33
  483. data/lib/sequel/adapters/odbc/progress.rb +0 -10
  484. data/lib/sequel/adapters/shared/cubrid.rb +0 -245
  485. data/lib/sequel/adapters/shared/firebird.rb +0 -247
  486. data/lib/sequel/adapters/shared/informix.rb +0 -54
  487. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -152
  488. data/lib/sequel/adapters/shared/progress.rb +0 -40
  489. data/lib/sequel/adapters/swift/mysql.rb +0 -49
  490. data/lib/sequel/adapters/swift/postgres.rb +0 -47
  491. data/lib/sequel/adapters/swift/sqlite.rb +0 -49
  492. data/lib/sequel/adapters/swift.rb +0 -160
  493. data/lib/sequel/adapters/utils/pg_types.rb +0 -70
  494. data/lib/sequel/dataset/mutation.rb +0 -111
  495. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -5
  496. data/lib/sequel/extensions/filter_having.rb +0 -63
  497. data/lib/sequel/extensions/hash_aliases.rb +0 -49
  498. data/lib/sequel/extensions/meta_def.rb +0 -35
  499. data/lib/sequel/extensions/query_literals.rb +0 -84
  500. data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -24
  501. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -122
  502. data/lib/sequel/extensions/set_overrides.rb +0 -76
  503. data/lib/sequel/no_core_ext.rb +0 -3
  504. data/lib/sequel/plugins/association_autoreloading.rb +0 -9
  505. data/lib/sequel/plugins/identifier_columns.rb +0 -47
  506. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -9
  507. data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -81
  508. data/lib/sequel/plugins/prepared_statements_associations.rb +0 -119
  509. data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -61
  510. data/lib/sequel/plugins/schema.rb +0 -82
  511. data/lib/sequel/plugins/scissors.rb +0 -35
  512. data/spec/adapter_spec.rb +0 -4
  513. data/spec/adapters/db2_spec.rb +0 -160
  514. data/spec/adapters/firebird_spec.rb +0 -411
  515. data/spec/adapters/informix_spec.rb +0 -100
  516. data/spec/adapters/mssql_spec.rb +0 -733
  517. data/spec/adapters/mysql_spec.rb +0 -1319
  518. data/spec/adapters/oracle_spec.rb +0 -313
  519. data/spec/adapters/postgres_spec.rb +0 -3790
  520. data/spec/adapters/spec_helper.rb +0 -49
  521. data/spec/adapters/sqlanywhere_spec.rb +0 -170
  522. data/spec/adapters/sqlite_spec.rb +0 -688
  523. data/spec/bin_spec.rb +0 -258
  524. data/spec/core/connection_pool_spec.rb +0 -1045
  525. data/spec/core/database_spec.rb +0 -2636
  526. data/spec/core/dataset_spec.rb +0 -5175
  527. data/spec/core/deprecated_spec.rb +0 -70
  528. data/spec/core/expression_filters_spec.rb +0 -1247
  529. data/spec/core/mock_adapter_spec.rb +0 -464
  530. data/spec/core/object_graph_spec.rb +0 -303
  531. data/spec/core/placeholder_literalizer_spec.rb +0 -163
  532. data/spec/core/schema_generator_spec.rb +0 -203
  533. data/spec/core/schema_spec.rb +0 -1676
  534. data/spec/core/spec_helper.rb +0 -34
  535. data/spec/core/version_spec.rb +0 -7
  536. data/spec/core_extensions_spec.rb +0 -699
  537. data/spec/core_model_spec.rb +0 -2
  538. data/spec/core_spec.rb +0 -1
  539. data/spec/extensions/accessed_columns_spec.rb +0 -51
  540. data/spec/extensions/active_model_spec.rb +0 -85
  541. data/spec/extensions/after_initialize_spec.rb +0 -24
  542. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  543. data/spec/extensions/association_dependencies_spec.rb +0 -117
  544. data/spec/extensions/association_pks_spec.rb +0 -405
  545. data/spec/extensions/association_proxies_spec.rb +0 -86
  546. data/spec/extensions/auto_validations_spec.rb +0 -192
  547. data/spec/extensions/before_after_save_spec.rb +0 -40
  548. data/spec/extensions/blacklist_security_spec.rb +0 -88
  549. data/spec/extensions/blank_spec.rb +0 -69
  550. data/spec/extensions/boolean_readers_spec.rb +0 -93
  551. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  552. data/spec/extensions/caching_spec.rb +0 -270
  553. data/spec/extensions/class_table_inheritance_spec.rb +0 -444
  554. data/spec/extensions/column_conflicts_spec.rb +0 -60
  555. data/spec/extensions/column_select_spec.rb +0 -108
  556. data/spec/extensions/columns_introspection_spec.rb +0 -91
  557. data/spec/extensions/composition_spec.rb +0 -242
  558. data/spec/extensions/connection_expiration_spec.rb +0 -121
  559. data/spec/extensions/connection_validator_spec.rb +0 -127
  560. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -288
  561. data/spec/extensions/constraint_validations_spec.rb +0 -389
  562. data/spec/extensions/core_refinements_spec.rb +0 -519
  563. data/spec/extensions/csv_serializer_spec.rb +0 -180
  564. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  565. data/spec/extensions/dataset_associations_spec.rb +0 -343
  566. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  567. data/spec/extensions/date_arithmetic_spec.rb +0 -167
  568. data/spec/extensions/defaults_setter_spec.rb +0 -102
  569. data/spec/extensions/delay_add_association_spec.rb +0 -74
  570. data/spec/extensions/dirty_spec.rb +0 -180
  571. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -110
  572. data/spec/extensions/eager_each_spec.rb +0 -66
  573. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  574. data/spec/extensions/error_splitter_spec.rb +0 -18
  575. data/spec/extensions/error_sql_spec.rb +0 -20
  576. data/spec/extensions/eval_inspect_spec.rb +0 -73
  577. data/spec/extensions/filter_having_spec.rb +0 -40
  578. data/spec/extensions/force_encoding_spec.rb +0 -114
  579. data/spec/extensions/from_block_spec.rb +0 -21
  580. data/spec/extensions/graph_each_spec.rb +0 -119
  581. data/spec/extensions/hash_aliases_spec.rb +0 -24
  582. data/spec/extensions/hook_class_methods_spec.rb +0 -429
  583. data/spec/extensions/identifier_columns_spec.rb +0 -17
  584. data/spec/extensions/inflector_spec.rb +0 -183
  585. data/spec/extensions/input_transformer_spec.rb +0 -54
  586. data/spec/extensions/insert_returning_select_spec.rb +0 -46
  587. data/spec/extensions/instance_filters_spec.rb +0 -79
  588. data/spec/extensions/instance_hooks_spec.rb +0 -276
  589. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  590. data/spec/extensions/json_serializer_spec.rb +0 -304
  591. data/spec/extensions/lazy_attributes_spec.rb +0 -170
  592. data/spec/extensions/list_spec.rb +0 -278
  593. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  594. data/spec/extensions/many_through_many_spec.rb +0 -2172
  595. data/spec/extensions/meta_def_spec.rb +0 -21
  596. data/spec/extensions/migration_spec.rb +0 -728
  597. data/spec/extensions/modification_detection_spec.rb +0 -80
  598. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
  599. data/spec/extensions/named_timezones_spec.rb +0 -108
  600. data/spec/extensions/nested_attributes_spec.rb +0 -697
  601. data/spec/extensions/no_auto_literal_strings_spec.rb +0 -65
  602. data/spec/extensions/null_dataset_spec.rb +0 -85
  603. data/spec/extensions/optimistic_locking_spec.rb +0 -128
  604. data/spec/extensions/pagination_spec.rb +0 -118
  605. data/spec/extensions/pg_array_associations_spec.rb +0 -736
  606. data/spec/extensions/pg_array_ops_spec.rb +0 -143
  607. data/spec/extensions/pg_array_spec.rb +0 -390
  608. data/spec/extensions/pg_enum_spec.rb +0 -92
  609. data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
  610. data/spec/extensions/pg_hstore_spec.rb +0 -206
  611. data/spec/extensions/pg_inet_ops_spec.rb +0 -101
  612. data/spec/extensions/pg_inet_spec.rb +0 -52
  613. data/spec/extensions/pg_interval_spec.rb +0 -76
  614. data/spec/extensions/pg_json_ops_spec.rb +0 -275
  615. data/spec/extensions/pg_json_spec.rb +0 -218
  616. data/spec/extensions/pg_loose_count_spec.rb +0 -17
  617. data/spec/extensions/pg_range_ops_spec.rb +0 -58
  618. data/spec/extensions/pg_range_spec.rb +0 -473
  619. data/spec/extensions/pg_row_ops_spec.rb +0 -60
  620. data/spec/extensions/pg_row_plugin_spec.rb +0 -62
  621. data/spec/extensions/pg_row_spec.rb +0 -360
  622. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
  623. data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
  624. data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
  625. data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
  626. data/spec/extensions/prepared_statements_spec.rb +0 -103
  627. data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
  628. data/spec/extensions/pretty_table_spec.rb +0 -92
  629. data/spec/extensions/query_literals_spec.rb +0 -183
  630. data/spec/extensions/query_spec.rb +0 -102
  631. data/spec/extensions/rcte_tree_spec.rb +0 -392
  632. data/spec/extensions/round_timestamps_spec.rb +0 -43
  633. data/spec/extensions/schema_caching_spec.rb +0 -41
  634. data/spec/extensions/schema_dumper_spec.rb +0 -814
  635. data/spec/extensions/schema_spec.rb +0 -117
  636. data/spec/extensions/scissors_spec.rb +0 -26
  637. data/spec/extensions/select_remove_spec.rb +0 -38
  638. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
  639. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  640. data/spec/extensions/serialization_spec.rb +0 -362
  641. data/spec/extensions/server_block_spec.rb +0 -90
  642. data/spec/extensions/server_logging_spec.rb +0 -45
  643. data/spec/extensions/set_overrides_spec.rb +0 -61
  644. data/spec/extensions/sharding_spec.rb +0 -198
  645. data/spec/extensions/shared_caching_spec.rb +0 -175
  646. data/spec/extensions/single_table_inheritance_spec.rb +0 -297
  647. data/spec/extensions/singular_table_names_spec.rb +0 -22
  648. data/spec/extensions/skip_create_refresh_spec.rb +0 -17
  649. data/spec/extensions/spec_helper.rb +0 -71
  650. data/spec/extensions/split_array_nil_spec.rb +0 -24
  651. data/spec/extensions/split_values_spec.rb +0 -22
  652. data/spec/extensions/sql_comments_spec.rb +0 -27
  653. data/spec/extensions/sql_expr_spec.rb +0 -60
  654. data/spec/extensions/static_cache_spec.rb +0 -361
  655. data/spec/extensions/string_agg_spec.rb +0 -85
  656. data/spec/extensions/string_date_time_spec.rb +0 -95
  657. data/spec/extensions/string_stripper_spec.rb +0 -68
  658. data/spec/extensions/subclasses_spec.rb +0 -66
  659. data/spec/extensions/subset_conditions_spec.rb +0 -38
  660. data/spec/extensions/table_select_spec.rb +0 -71
  661. data/spec/extensions/tactical_eager_loading_spec.rb +0 -136
  662. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  663. data/spec/extensions/timestamps_spec.rb +0 -175
  664. data/spec/extensions/to_dot_spec.rb +0 -154
  665. data/spec/extensions/touch_spec.rb +0 -203
  666. data/spec/extensions/tree_spec.rb +0 -274
  667. data/spec/extensions/typecast_on_load_spec.rb +0 -80
  668. data/spec/extensions/unlimited_update_spec.rb +0 -20
  669. data/spec/extensions/update_or_create_spec.rb +0 -87
  670. data/spec/extensions/update_primary_key_spec.rb +0 -100
  671. data/spec/extensions/update_refresh_spec.rb +0 -53
  672. data/spec/extensions/uuid_spec.rb +0 -106
  673. data/spec/extensions/validate_associated_spec.rb +0 -52
  674. data/spec/extensions/validation_class_methods_spec.rb +0 -1027
  675. data/spec/extensions/validation_helpers_spec.rb +0 -554
  676. data/spec/extensions/xml_serializer_spec.rb +0 -207
  677. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  678. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  679. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  680. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  681. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  682. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  683. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  684. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  685. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  686. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  687. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  688. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  689. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  690. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  691. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  692. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  693. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  694. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  695. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  696. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  697. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  698. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  699. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  700. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  701. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  702. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  703. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  704. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  705. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  706. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  707. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  708. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  709. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  710. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  711. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  712. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  713. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  714. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  715. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  716. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  717. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  718. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  719. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  720. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  721. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  722. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  723. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  724. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  725. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  726. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  727. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  728. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  729. data/spec/guards_helper.rb +0 -55
  730. data/spec/integration/associations_test.rb +0 -2506
  731. data/spec/integration/database_test.rb +0 -113
  732. data/spec/integration/dataset_test.rb +0 -1858
  733. data/spec/integration/eager_loader_test.rb +0 -687
  734. data/spec/integration/migrator_test.rb +0 -262
  735. data/spec/integration/model_test.rb +0 -230
  736. data/spec/integration/plugin_test.rb +0 -2297
  737. data/spec/integration/prepared_statement_test.rb +0 -467
  738. data/spec/integration/schema_test.rb +0 -815
  739. data/spec/integration/spec_helper.rb +0 -56
  740. data/spec/integration/timezone_test.rb +0 -86
  741. data/spec/integration/transaction_test.rb +0 -406
  742. data/spec/integration/type_test.rb +0 -133
  743. data/spec/model/association_reflection_spec.rb +0 -565
  744. data/spec/model/associations_spec.rb +0 -4589
  745. data/spec/model/base_spec.rb +0 -759
  746. data/spec/model/class_dataset_methods_spec.rb +0 -150
  747. data/spec/model/dataset_methods_spec.rb +0 -149
  748. data/spec/model/eager_loading_spec.rb +0 -2197
  749. data/spec/model/hooks_spec.rb +0 -604
  750. data/spec/model/inflector_spec.rb +0 -26
  751. data/spec/model/model_spec.rb +0 -1097
  752. data/spec/model/plugins_spec.rb +0 -299
  753. data/spec/model/record_spec.rb +0 -2162
  754. data/spec/model/spec_helper.rb +0 -46
  755. data/spec/model/validations_spec.rb +0 -193
  756. data/spec/model_no_assoc_spec.rb +0 -1
  757. data/spec/model_spec.rb +0 -1
  758. data/spec/plugin_spec.rb +0 -1
  759. data/spec/sequel_coverage.rb +0 -15
  760. data/spec/spec_config.rb +0 -10
@@ -1,3790 +0,0 @@
1
- SEQUEL_ADAPTER_TEST = :postgres
2
-
3
- require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
4
-
5
- def DB.sqls
6
- (@sqls ||= [])
7
- end
8
- logger = Object.new
9
- def logger.method_missing(m, msg)
10
- DB.sqls << msg
11
- end
12
- DB.loggers << logger
13
-
14
- DB.extension :pg_array, :pg_hstore, :pg_range, :pg_row, :pg_inet, :pg_json, :pg_enum
15
- begin
16
- DB.extension :pg_interval
17
- rescue LoadError
18
- end
19
-
20
- describe "PostgreSQL", '#create_table' do
21
- before do
22
- @db = DB
23
- @db.test_connection
24
- DB.sqls.clear
25
- end
26
- after do
27
- @db.drop_table?(:tmp_dolls, :unlogged_dolls)
28
- end
29
-
30
- it "should create a temporary table" do
31
- @db.create_table(:tmp_dolls, :temp => true){text :name}
32
- check_sqls do
33
- @db.sqls.must_equal ['CREATE TEMPORARY TABLE "tmp_dolls" ("name" text)']
34
- end
35
- end
36
-
37
- it "temporary table should support :on_commit option" do
38
- @db.drop_table?(:some_table)
39
- @db.transaction do
40
- @db.create_table(:some_table, :temp => true, :on_commit => :drop){text :name}
41
- end
42
- @db.table_exists?(:some_table).must_equal false
43
-
44
- @db.transaction do
45
- @db.create_table(:some_table, :temp => true, :on_commit => :delete_rows){text :name}
46
- @db[:some_table].insert('a')
47
- end
48
- @db.table_exists?(:some_table).must_equal true
49
- @db[:some_table].empty?.must_equal true
50
-
51
- @db.drop_table(:some_table)
52
- @db.transaction do
53
- @db.create_table(:some_table, :temp => true, :on_commit => :preserve_rows){text :name}
54
- @db[:some_table].insert('a')
55
- end
56
- @db.table_exists?(:some_table).must_equal true
57
- @db[:some_table].count.must_equal 1
58
- @db.drop_table(:some_table)
59
- end
60
-
61
- it "temporary table should accept :on_commit with :as option" do
62
- @db.drop_table?(:some_table)
63
- @db.transaction do
64
- @db.create_table(:some_table, :temp => true, :on_commit => :drop, :as => 'select 1')
65
- end
66
- @db.table_exists?(:some_table).must_equal false
67
- end
68
-
69
- it ":on_commit should raise error if not used on a temporary table" do
70
- proc{@db.create_table(:some_table, :on_commit => :drop)}.must_raise(Sequel::Error)
71
- end
72
-
73
- it ":on_commit should raise error if given unsupported value" do
74
- proc{@db.create_table(:some_table, :temp => true, :on_commit => :unsupported){text :name}}.must_raise(Sequel::Error)
75
- end
76
-
77
- it "should create an unlogged table" do
78
- @db.create_table(:unlogged_dolls, :unlogged => true){text :name}
79
- check_sqls do
80
- @db.sqls.must_equal ['CREATE UNLOGGED TABLE "unlogged_dolls" ("name" text)']
81
- end
82
- end
83
-
84
- it "should create a table inheriting from another table" do
85
- @db.create_table(:unlogged_dolls){text :name}
86
- @db.create_table(:tmp_dolls, :inherits=>:unlogged_dolls){}
87
- @db[:tmp_dolls].insert('a')
88
- @db[:unlogged_dolls].all.must_equal [{:name=>'a'}]
89
- end
90
-
91
- it "should create a table inheriting from multiple tables" do
92
- begin
93
- @db.create_table(:unlogged_dolls){text :name}
94
- @db.create_table(:tmp_dolls){text :bar}
95
- @db.create_table!(:items, :inherits=>[:unlogged_dolls, :tmp_dolls]){text :foo}
96
- @db[:items].insert(:name=>'a', :bar=>'b', :foo=>'c')
97
- @db[:unlogged_dolls].all.must_equal [{:name=>'a'}]
98
- @db[:tmp_dolls].all.must_equal [{:bar=>'b'}]
99
- @db[:items].all.must_equal [{:name=>'a', :bar=>'b', :foo=>'c'}]
100
- ensure
101
- @db.drop_table?(:items)
102
- end
103
- end
104
-
105
- it "should not allow to pass both :temp and :unlogged" do
106
- proc do
107
- @db.create_table(:temp_unlogged_dolls, :temp => true, :unlogged => true){text :name}
108
- end.must_raise(Sequel::Error, "can't provide both :temp and :unlogged to create_table")
109
- end
110
-
111
- it "should support :if_exists option to drop_column" do
112
- @db.create_table(:tmp_dolls){Integer :a; Integer :b}
113
- 2.times do
114
- @db.drop_column :tmp_dolls, :b, :if_exists=>true
115
- @db[:tmp_dolls].columns.must_equal [:a]
116
- end
117
- end if DB.server_version >= 90000
118
-
119
- it "should support pg_loose_count extension" do
120
- @db.extension :pg_loose_count
121
- @db.create_table(:tmp_dolls){text :name}
122
- @db.loose_count(:tmp_dolls).must_be_kind_of(Integer)
123
- @db.loose_count(:tmp_dolls).must_equal 0
124
- @db.loose_count(:public__tmp_dolls).must_equal 0
125
- @db[:tmp_dolls].insert('a')
126
- @db << 'VACUUM ANALYZE tmp_dolls'
127
- @db.loose_count(:tmp_dolls).must_equal 1
128
- @db.loose_count(:public__tmp_dolls).must_equal 1
129
- end
130
- end
131
-
132
- describe "PostgreSQL views" do
133
- before do
134
- @db = DB
135
- @db.drop_table?(:items, :cascade=>true)
136
- @db.create_table(:items){Integer :number}
137
- @db[:items].insert(10)
138
- @db[:items].insert(20)
139
- end
140
- after do
141
- @opts ||={}
142
- @db.drop_view(:items_view, @opts.merge(:if_exists=>true, :cascade=>true)) rescue nil
143
- @db.drop_table?(:items)
144
- end
145
-
146
- it "should support temporary views" do
147
- @db.create_view(:items_view, @db[:items].where(:number=>10), :temp=>true)
148
- @db[:items_view].map(:number).must_equal [10]
149
- @db.create_or_replace_view(:items_view, @db[:items].where(:number=>20), :temp=>true)
150
- @db[:items_view].map(:number).must_equal [20]
151
- end
152
-
153
- it "should support recursive views" do
154
- @db.create_view(:items_view, @db[:items].where(:number=>10).union(@db[:items, :items_view].where(Sequel.-(:number, 5)=>:n).select(:number), :all=>true, :from_self=>false), :recursive=>[:n])
155
- @db[:items_view].select_order_map(:n).must_equal [10]
156
- @db[:items].insert(15)
157
- @db[:items_view].select_order_map(:n).must_equal [10, 15, 20]
158
- end if DB.server_version >= 90300
159
-
160
- it "should support materialized views" do
161
- @opts = {:materialized=>true}
162
- @db.create_view(:items_view, @db[:items].where{number >= 10}, @opts)
163
- @db[:items_view].select_order_map(:number).must_equal [10, 20]
164
- @db[:items].insert(15)
165
- @db[:items_view].select_order_map(:number).must_equal [10, 20]
166
- @db.refresh_view(:items_view)
167
- @db[:items_view].select_order_map(:number).must_equal [10, 15, 20]
168
- end if DB.server_version >= 90300
169
-
170
- it "should support refreshing materialized views concurrently" do
171
- @opts = {:materialized=>true}
172
- @db.create_view(:items_view, @db[:items].where{number >= 10}, @opts)
173
- @db.refresh_view(:items_view)
174
- proc{@db.refresh_view(:items_view, :concurrently=>true)}.must_raise(Sequel::DatabaseError)
175
- @db.add_index :items_view, :number, :unique=>true
176
- @db.refresh_view(:items_view, :concurrently=>true)
177
- end if DB.server_version >= 90400
178
-
179
- it "should support :if_exists=>true for not raising an error if the view does not exist" do
180
- @db.drop_view(:items_view, :if_exists=>true)
181
- end
182
- end
183
-
184
- describe "PostgreSQL", 'INSERT ON CONFLICT' do
185
- before(:all) do
186
- @db = DB
187
- @db.create_table!(:ic_test){Integer :a; Integer :b; Integer :c; unique :a, :name=>:ic_test_a_uidx; unique [:b, :c], :name=>:ic_test_b_c_uidx}
188
- @ds = @db[:ic_test]
189
- end
190
- before do
191
- @ds.delete
192
- end
193
- after(:all) do
194
- @db.drop_table?(:ic_test)
195
- end
196
-
197
- it "Dataset#insert_ignore and insert_conflict should ignore uniqueness violations" do
198
- @ds.insert(1, 2, 3)
199
- proc{@ds.insert(1, 3, 4)}.must_raise Sequel::UniqueConstraintViolation
200
- @ds.insert_ignore.insert(1, 3, 4).must_equal nil
201
- @ds.insert_conflict.insert(1, 3, 4).must_equal nil
202
- @ds.insert_conflict(:target=>:a).insert(1, 3, 4).must_equal nil
203
- @ds.insert_conflict(:constraint=>:ic_test_a_uidx).insert(1, 3, 4).must_equal nil
204
- @ds.all.must_equal [{:a=>1, :b=>2, :c=>3}]
205
- end
206
-
207
- it "Dataset#insert_conflict should handle upserts" do
208
- @ds.insert(1, 2, 3)
209
- @ds.insert_conflict(:target=>:a, :update=>{:b=>3}).insert(1, 3, 4).must_equal nil
210
- @ds.all.must_equal [{:a=>1, :b=>3, :c=>3}]
211
- @ds.insert_conflict(:target=>[:b, :c], :update=>{:c=>5}).insert(5, 3, 3).must_equal nil
212
- @ds.all.must_equal [{:a=>1, :b=>3, :c=>5}]
213
- @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>4}).insert(1, 3).must_equal nil
214
- @ds.all.must_equal [{:a=>1, :b=>4, :c=>5}]
215
- @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>5}, :update_where=>{:ic_test__b=>4}).insert(1, 3, 4).must_equal nil
216
- @ds.all.must_equal [{:a=>1, :b=>5, :c=>5}]
217
- @ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>6}, :update_where=>{:ic_test__b=>4}).insert(1, 3, 4).must_equal nil
218
- @ds.all.must_equal [{:a=>1, :b=>5, :c=>5}]
219
- end
220
-
221
- it "Dataset#insert_conflict should respect expressions in the target argument" do
222
- @ds.insert_conflict(:target=>:a).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"a\") DO NOTHING"
223
- @ds.insert_conflict(:target=>[:b, :c]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", \"c\") DO NOTHING"
224
- @ds.insert_conflict(:target=>[:b, Sequel.function(:round, :c)]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", round(\"c\")) DO NOTHING"
225
- @ds.insert_conflict(:target=>[:b, Sequel.virtual_row{|o| o.round(:c)}]).insert_sql(1, 2, 3).must_equal "INSERT INTO \"ic_test\" VALUES (1, 2, 3) ON CONFLICT (\"b\", round(\"c\")) DO NOTHING"
226
- end
227
- end if DB.server_version >= 90500
228
-
229
- describe "A PostgreSQL database" do
230
- before(:all) do
231
- @db = DB
232
- @db.create_table!(:public__testfk){primary_key :id; foreign_key :i, :public__testfk}
233
- end
234
- after(:all) do
235
- @db.drop_table?(:public__testfk)
236
- end
237
-
238
- it "should provide the server version" do
239
- @db.server_version.must_be :>, 70000
240
- end
241
-
242
- it "should create a dataset using the VALUES clause via #values" do
243
- @db.values([[1, 2], [3, 4]]).map([:column1, :column2]).must_equal [[1, 2], [3, 4]]
244
- end
245
-
246
- it "should support ordering in aggregate functions" do
247
- @db.from(@db.values([['1'], ['2']]).as(:t, [:a])).get{string_agg(:a, '-').order(Sequel.desc(:a)).as(:c)}.must_equal '2-1'
248
- end if DB.server_version >= 90000
249
-
250
- it "should support ordering and limiting with #values" do
251
- @db.values([[1, 2], [3, 4]]).reverse(:column2, :column1).limit(1).map([:column1, :column2]).must_equal [[3, 4]]
252
- @db.values([[1, 2], [3, 4]]).reverse(:column2, :column1).offset(1).map([:column1, :column2]).must_equal [[1, 2]]
253
- end
254
-
255
- it "should support subqueries with #values" do
256
- @db.values([[1, 2]]).from_self.cross_join(@db.values([[3, 4]]).as(:x, [:c1, :c2])).map([:column1, :column2, :c1, :c2]).must_equal [[1, 2, 3, 4]]
257
- end
258
-
259
- it "should respect the :read_only option per-savepoint" do
260
- proc{@db.transaction{@db.transaction(:savepoint=>true, :read_only=>true){@db[:public__testfk].insert}}}.must_raise(Sequel::DatabaseError)
261
- proc{@db.transaction(:auto_savepoint=>true, :read_only=>true){@db.transaction(:read_only=>false){@db[:public__testfk].insert}}}.must_raise(Sequel::DatabaseError)
262
- @db[:public__testfk].delete
263
- @db.transaction{@db[:public__testfk].insert; @db.transaction(:savepoint=>true, :read_only=>true){@db[:public__testfk].all;}}
264
- @db.transaction{@db.transaction(:savepoint=>true, :read_only=>true){}; @db[:public__testfk].insert}
265
- @db.transaction{@db[:public__testfk].all; @db.transaction(:savepoint=>true, :read_only=>true){@db[:public__testfk].all;}}
266
- end
267
-
268
- it "should support disable_insert_returning" do
269
- ds = @db[:public__testfk].disable_insert_returning
270
- ds.delete
271
- ds.insert.must_equal nil
272
- id = ds.max(:id)
273
- ds.select_order_map([:id, :i]).must_equal [[id, nil]]
274
- ds.insert(:i=>id).must_equal nil
275
- ds.select_order_map([:id, :i]).must_equal [[id, nil], [id+1, id]]
276
- ds.insert_select(:i=>ds.max(:id)).must_equal nil
277
- ds.select_order_map([:id, :i]).must_equal [[id, nil], [id+1, id]]
278
- c = Class.new(Sequel::Model(ds))
279
- c.class_eval do
280
- def before_create
281
- self.id = model.max(:id)+1
282
- super
283
- end
284
- end
285
- c.create(:i=>id+1).must_equal c.load(:id=>id+2, :i=>id+1)
286
- ds.select_order_map([:id, :i]).must_equal [[id, nil], [id+1, id], [id+2, id+1]]
287
- ds.delete
288
- end
289
-
290
- it "should support functions with and without quoting" do
291
- ds = @db[:public__testfk]
292
- ds.delete
293
- ds.insert
294
- ds.get{sum(1)}.must_equal 1
295
- ds.get{Sequel.function('pg_catalog.sum', 1)}.must_equal 1
296
- ds.get{sum.function(1)}.must_equal 1
297
- ds.get{pg_catalog__sum.function(1)}.must_equal 1
298
- ds.delete
299
- end
300
-
301
- it "should support a :qualify option to tables and views" do
302
- @db.tables(:qualify=>true).must_include(Sequel.qualify(:public, :testfk))
303
- begin
304
- @db.create_view(:testfkv, @db[:testfk])
305
- @db.views(:qualify=>true).must_include(Sequel.qualify(:public, :testfkv))
306
- ensure
307
- @db.drop_view(:testfkv)
308
- end
309
- end
310
-
311
- it "should not typecast the int2vector type incorrectly" do
312
- @db.get(Sequel.cast('10 20', :int2vector)).wont_equal 10
313
- end
314
-
315
- cspecify "should not typecast the money type incorrectly", [:do] do
316
- @db.get(Sequel.cast('10.01', :money)).wont_equal 0
317
- end
318
-
319
- it "should correctly parse the schema" do
320
- @db.schema(:public__testfk, :reload=>true).map{|c,s| [c, s[:oid]]}.must_equal [[:id, 23], [:i, 23]]
321
- end
322
-
323
- it "should parse foreign keys for tables in a schema" do
324
- @db.foreign_key_list(:public__testfk).must_equal [{:on_delete=>:no_action, :on_update=>:no_action, :columns=>[:i], :key=>[:id], :deferrable=>false, :table=>Sequel.qualify(:public, :testfk), :name=>:testfk_i_fkey}]
325
- end
326
-
327
- it "should return uuid fields as strings" do
328
- @db.get(Sequel.cast('550e8400-e29b-41d4-a716-446655440000', :uuid)).must_equal '550e8400-e29b-41d4-a716-446655440000'
329
- end
330
-
331
- it "should handle inserts with placeholder literal string tables" do
332
- ds = @db.from(Sequel.lit('?', :testfk))
333
- ds.delete
334
- ds.insert(:id=>1)
335
- ds.select_map(:id).must_equal [1]
336
- end
337
-
338
- it "should have notice receiver receive notices" do
339
- a = nil
340
- Sequel.connect(DB.opts.merge(:notice_receiver=>proc{|r| a = r.result_error_message})){|db| db.do("BEGIN\nRAISE WARNING 'foo';\nEND;")}
341
- a.must_equal "WARNING: foo\n"
342
- end if DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && DB.server_version >= 90000
343
-
344
- # These only test the SQL created, because a true test using file_fdw or postgres_fdw
345
- # requires superuser permissions, and you should not be running the tests as a superuser.
346
- it "should support creating and dropping foreign tables" do
347
- DB.send(:create_table_sql, :t, DB.create_table_generator{Integer :a}, :foreign=>:f, :options=>{:o=>1}).must_equal 'CREATE FOREIGN TABLE "t" ("a" integer) SERVER "f" OPTIONS (o \'1\')'
348
- DB.send(:drop_table_sql, :t, :foreign=>true).must_equal 'DROP FOREIGN TABLE "t"'
349
- end
350
- end
351
-
352
- describe "A PostgreSQL database with domain types" do
353
- before(:all) do
354
- @db = DB
355
- @db << "DROP DOMAIN IF EXISTS positive_number CASCADE"
356
- @db << "CREATE DOMAIN positive_number AS numeric(10,2) CHECK (VALUE > 0)"
357
- @db.create_table!(:testfk){positive_number :id, :primary_key=>true}
358
- end
359
- after(:all) do
360
- @db.drop_table?(:testfk)
361
- @db << "DROP DOMAIN positive_number"
362
- end
363
-
364
- it "should correctly parse the schema" do
365
- sch = @db.schema(:testfk, :reload=>true)
366
- sch.first.last.delete(:domain_oid).must_be_kind_of(Integer)
367
- sch.first.last[:db_domain_type].must_equal 'positive_number'
368
- end
369
- end
370
-
371
- describe "A PostgreSQL dataset" do
372
- before(:all) do
373
- @db = DB
374
- @d = @db[:test]
375
- @db.create_table! :test do
376
- text :name
377
- integer :value, :index => true
378
- end
379
- end
380
- before do
381
- @d.delete
382
- @db.sqls.clear
383
- end
384
- after do
385
- @db.drop_table?(:atest)
386
- end
387
- after(:all) do
388
- @db.drop_table?(:test)
389
- end
390
-
391
- it "should quote columns and tables using double quotes if quoting identifiers" do
392
- check_sqls do
393
- @d.select(:name).sql.must_equal 'SELECT "name" FROM "test"'
394
- @d.select(Sequel.lit('COUNT(*)')).sql.must_equal 'SELECT COUNT(*) FROM "test"'
395
- @d.select(Sequel.function(:max, :value)).sql.must_equal 'SELECT max("value") FROM "test"'
396
- @d.select(Sequel.function(:NOW)).sql.must_equal 'SELECT NOW() FROM "test"'
397
- @d.select(Sequel.function(:max, :items__value)).sql.must_equal 'SELECT max("items"."value") FROM "test"'
398
- @d.order(Sequel.desc(:name)).sql.must_equal 'SELECT * FROM "test" ORDER BY "name" DESC'
399
- @d.select(Sequel.lit('test.name AS item_name')).sql.must_equal 'SELECT test.name AS item_name FROM "test"'
400
- @d.select(Sequel.lit('"name"')).sql.must_equal 'SELECT "name" FROM "test"'
401
- @d.select(Sequel.lit('max(test."name") AS "max_name"')).sql.must_equal 'SELECT max(test."name") AS "max_name" FROM "test"'
402
- @d.insert_sql(:x => :y).must_match(/\AINSERT INTO "test" \("x"\) VALUES \("y"\)( RETURNING NULL)?\z/)
403
-
404
- @d.select(Sequel.function(:test, :abc, 'hello')).sql.must_equal "SELECT test(\"abc\", 'hello') FROM \"test\""
405
- @d.select(Sequel.function(:test, :abc__def, 'hello')).sql.must_equal "SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
406
- @d.select(Sequel.function(:test, :abc__def, 'hello').as(:x2)).sql.must_equal "SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
407
- @d.insert_sql(:value => 333).must_match(/\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/)
408
- end
409
- end
410
-
411
- it "should quote fields correctly when reversing the order if quoting identifiers" do
412
- check_sqls do
413
- @d.reverse_order(:name).sql.must_equal 'SELECT * FROM "test" ORDER BY "name" DESC'
414
- @d.reverse_order(Sequel.desc(:name)).sql.must_equal 'SELECT * FROM "test" ORDER BY "name" ASC'
415
- @d.reverse_order(:name, Sequel.desc(:test)).sql.must_equal 'SELECT * FROM "test" ORDER BY "name" DESC, "test" ASC'
416
- @d.reverse_order(Sequel.desc(:name), :test).sql.must_equal 'SELECT * FROM "test" ORDER BY "name" ASC, "test" DESC'
417
- end
418
- end
419
-
420
- it "should support regexps" do
421
- @d << {:name => 'abc', :value => 1}
422
- @d << {:name => 'bcd', :value => 2}
423
- @d.filter(:name => /bc/).count.must_equal 2
424
- @d.filter(:name => /^bc/).count.must_equal 1
425
- end
426
-
427
- it "should support NULLS FIRST and NULLS LAST" do
428
- @d << {:name => 'abc'}
429
- @d << {:name => 'bcd'}
430
- @d << {:name => 'bcd', :value => 2}
431
- @d.order(Sequel.asc(:value, :nulls=>:first), :name).select_map(:name).must_equal %w[abc bcd bcd]
432
- @d.order(Sequel.asc(:value, :nulls=>:last), :name).select_map(:name).must_equal %w[bcd abc bcd]
433
- @d.order(Sequel.asc(:value, :nulls=>:first), :name).reverse.select_map(:name).must_equal %w[bcd bcd abc]
434
- end
435
-
436
- it "should support selecting from LATERAL functions" do
437
- @d.from{[generate_series(1,3,1).as(:a), pow(:a, 2).lateral.as(:b)]}.select_map([:a, :b])== [[1, 1], [2, 4], [3, 9]]
438
- end if DB.server_version >= 90300
439
-
440
- it "should support ordered-set and hypothetical-set aggregate functions" do
441
- @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{mode{}.within_group(:a)}.must_equal 1
442
- end if DB.server_version >= 90400
443
-
444
- it "should support filtered aggregate functions" do
445
- @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{count(:a).filter(:a=>1)}.must_equal 2
446
- end if DB.server_version >= 90400
447
-
448
- it "should support functions with ordinality" do
449
- @d.from{generate_series(1,10,3).with_ordinality}.select_map([:generate_series, :ordinality]).must_equal [[1, 1], [4, 2], [7, 3], [10, 4]]
450
- end if DB.server_version >= 90400
451
-
452
- it "#lock should lock tables and yield if a block is given" do
453
- @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
454
- end
455
-
456
- it "should support exclusion constraints when creating or altering tables" do
457
- @db.create_table!(:atest){Integer :t; exclude [[Sequel.desc(:t, :nulls=>:last), '=']], :using=>:btree, :where=>proc{t > 0}}
458
- @db[:atest].insert(1)
459
- @db[:atest].insert(2)
460
- proc{@db[:atest].insert(2)}.must_raise(Sequel::Postgres::ExclusionConstraintViolation)
461
-
462
- @db.create_table!(:atest){Integer :t}
463
- @db.alter_table(:atest){add_exclusion_constraint [[:t, '=']], :using=>:btree, :name=>'atest_ex'}
464
- @db[:atest].insert(1)
465
- @db[:atest].insert(2)
466
- proc{@db[:atest].insert(2)}.must_raise(Sequel::Postgres::ExclusionConstraintViolation)
467
- @db.alter_table(:atest){drop_constraint 'atest_ex'}
468
- end if DB.server_version >= 90000
469
-
470
- it "should support deferrable exclusion constraints" do
471
- @db.create_table!(:atest){Integer :t; exclude [[Sequel.desc(:t, :nulls=>:last), '=']], :using=>:btree, :where=>proc{t > 0}, :deferrable => true}
472
- proc do
473
- @db.transaction do
474
- @db[:atest].insert(2)
475
- @db[:atest].insert(2)
476
- end
477
- end.must_raise(Sequel::Postgres::ExclusionConstraintViolation)
478
- end if DB.server_version >= 90000
479
-
480
- it "should support Database#error_info for getting info hash on the given error" do
481
- @db.create_table!(:atest){Integer :t; Integer :t2, :null=>false, :default=>1; constraint :f, :t=>0}
482
- begin
483
- @db[:atest].insert(1)
484
- rescue => e
485
- end
486
- e.wont_equal nil
487
- info = @db.error_info(e)
488
- info[:schema].must_equal 'public'
489
- info[:table].must_equal 'atest'
490
- info[:constraint].must_equal 'f'
491
- info[:column].must_equal nil
492
- info[:type].must_equal nil
493
-
494
- begin
495
- @db[:atest].insert(0, nil)
496
- rescue => e
497
- end
498
- e.wont_equal nil
499
- info = @db.error_info(e.wrapped_exception)
500
- info[:schema].must_equal 'public'
501
- info[:table].must_equal 'atest'
502
- info[:constraint].must_equal nil
503
- info[:column].must_equal 't2'
504
- info[:type].must_equal nil
505
- end if DB.server_version >= 90300 && DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && Object.const_defined?(:PG) && ::PG.const_defined?(:Constants) && ::PG::Constants.const_defined?(:PG_DIAG_SCHEMA_NAME)
506
-
507
- it "should support Database#do for executing anonymous code blocks" do
508
- @db.drop_table?(:btest)
509
- @db.do "BEGIN EXECUTE 'CREATE TABLE btest (a INTEGER)'; EXECUTE 'INSERT INTO btest VALUES (1)'; END"
510
- @db[:btest].select_map(:a).must_equal [1]
511
-
512
- @db.do "BEGIN EXECUTE 'DROP TABLE btest; CREATE TABLE atest (a INTEGER)'; EXECUTE 'INSERT INTO atest VALUES (1)'; END", :language=>:plpgsql
513
- @db[:atest].select_map(:a).must_equal [1]
514
- end if DB.server_version >= 90000
515
-
516
- it "should support adding foreign key constarints that are not yet valid, and validating them later" do
517
- @db.create_table!(:atest){primary_key :id; Integer :fk}
518
- @db[:atest].insert(1, 5)
519
- @db.alter_table(:atest){add_foreign_key [:fk], :atest, :not_valid=>true, :name=>:atest_fk}
520
- @db[:atest].insert(2, 1)
521
- proc{@db[:atest].insert(3, 4)}.must_raise(Sequel::ForeignKeyConstraintViolation)
522
-
523
- proc{@db.alter_table(:atest){validate_constraint :atest_fk}}.must_raise(Sequel::ForeignKeyConstraintViolation)
524
- @db[:atest].where(:id=>1).update(:fk=>2)
525
- @db.alter_table(:atest){validate_constraint :atest_fk}
526
- @db.alter_table(:atest){validate_constraint :atest_fk}
527
- end if DB.server_version >= 90200
528
-
529
- it "should support adding check constarints that are not yet valid, and validating them later" do
530
- @db.create_table!(:atest){Integer :a}
531
- @db[:atest].insert(5)
532
- @db.alter_table(:atest){add_constraint({:name=>:atest_check, :not_valid=>true}){a >= 10}}
533
- @db[:atest].insert(10)
534
- proc{@db[:atest].insert(6)}.must_raise(Sequel::CheckConstraintViolation)
535
-
536
- proc{@db.alter_table(:atest){validate_constraint :atest_check}}.must_raise(Sequel::CheckConstraintViolation, Sequel::DatabaseError)
537
- @db[:atest].where{a < 10}.update(:a=>Sequel.+(:a, 10))
538
- @db.alter_table(:atest){validate_constraint :atest_check}
539
- @db.alter_table(:atest){validate_constraint :atest_check}
540
- end if DB.server_version >= 90200
541
-
542
- it "should support :using when altering a column's type" do
543
- @db.create_table!(:atest){Integer :t}
544
- @db[:atest].insert(1262304000)
545
- @db.alter_table(:atest){set_column_type :t, Time, :using=>Sequel.cast('epoch', Time) + Sequel.cast('1 second', :interval) * :t}
546
- @db[:atest].get(Sequel.extract(:year, :t)).must_equal 2010
547
- end
548
-
549
- it "should support :using with a string when altering a column's type" do
550
- @db.create_table!(:atest){Integer :t}
551
- @db[:atest].insert(1262304000)
552
- @db.alter_table(:atest){set_column_type :t, Time, :using=>"'epoch'::timestamp + '1 second'::interval * t"}
553
- @db[:atest].get(Sequel.extract(:year, :t)).must_equal 2010
554
- end
555
-
556
- it "should be able to parse the default value for an interval type" do
557
- @db.create_table!(:atest){interval :t, :default=>'1 week'}
558
- @db.schema(:atest).first.last[:ruby_default].must_equal '7 days'
559
- end
560
-
561
- it "should have #transaction support various types of synchronous options" do
562
- @db.transaction(:synchronous=>:on){}
563
- @db.transaction(:synchronous=>true){}
564
- @db.transaction(:synchronous=>:off){}
565
- @db.transaction(:synchronous=>false){}
566
- @db.sqls.grep(/synchronous/).must_equal ["SET LOCAL synchronous_commit = on", "SET LOCAL synchronous_commit = on", "SET LOCAL synchronous_commit = off", "SET LOCAL synchronous_commit = off"]
567
-
568
- @db.sqls.clear
569
- @db.transaction(:synchronous=>nil){}
570
- check_sqls do
571
- @db.sqls.must_equal ['BEGIN', 'COMMIT']
572
- end
573
-
574
- if @db.server_version >= 90100
575
- @db.sqls.clear
576
- @db.transaction(:synchronous=>:local){}
577
- check_sqls do
578
- @db.sqls.grep(/synchronous/).must_equal ["SET LOCAL synchronous_commit = local"]
579
- end
580
-
581
- if @db.server_version >= 90200
582
- @db.sqls.clear
583
- @db.transaction(:synchronous=>:remote_write){}
584
- check_sqls do
585
- @db.sqls.grep(/synchronous/).must_equal ["SET LOCAL synchronous_commit = remote_write"]
586
- end
587
- end
588
- end
589
- end
590
-
591
- it "should have #transaction support read only transactions" do
592
- @db.transaction(:read_only=>true){}
593
- @db.transaction(:read_only=>false){}
594
- @db.transaction(:isolation=>:serializable, :read_only=>true){}
595
- @db.transaction(:isolation=>:serializable, :read_only=>false){}
596
- @db.sqls.grep(/READ/).must_equal ["SET TRANSACTION READ ONLY", "SET TRANSACTION READ WRITE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE"]
597
- end
598
-
599
- it "should have #transaction support deferrable transactions" do
600
- @db.transaction(:deferrable=>true){}
601
- @db.transaction(:deferrable=>false){}
602
- @db.transaction(:deferrable=>true, :read_only=>true){}
603
- @db.transaction(:deferrable=>false, :read_only=>false){}
604
- @db.transaction(:isolation=>:serializable, :deferrable=>true, :read_only=>true){}
605
- @db.transaction(:isolation=>:serializable, :deferrable=>false, :read_only=>false){}
606
- @db.sqls.grep(/DEF/).must_equal ["SET TRANSACTION DEFERRABLE", "SET TRANSACTION NOT DEFERRABLE", "SET TRANSACTION READ ONLY DEFERRABLE", "SET TRANSACTION READ WRITE NOT DEFERRABLE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE NOT DEFERRABLE"]
607
- end if DB.server_version >= 90100
608
-
609
- it "should support creating indexes concurrently" do
610
- @db.add_index :test, [:name, :value], :concurrently=>true
611
- check_sqls do
612
- @db.sqls.must_equal ['CREATE INDEX CONCURRENTLY "test_name_value_index" ON "test" ("name", "value")']
613
- end
614
- end
615
-
616
- it "should support dropping indexes only if they already exist" do
617
- @db.add_index :test, [:name, :value], :name=>'tnv1'
618
- @db.sqls.clear
619
- @db.drop_index :test, [:name, :value], :if_exists=>true, :name=>'tnv1'
620
- check_sqls do
621
- @db.sqls.must_equal ['DROP INDEX IF EXISTS "tnv1"']
622
- end
623
- end
624
-
625
- it "should support CASCADE when dropping indexes" do
626
- @db.add_index :test, [:name, :value], :name=>'tnv2'
627
- @db.sqls.clear
628
- @db.drop_index :test, [:name, :value], :cascade=>true, :name=>'tnv2'
629
- check_sqls do
630
- @db.sqls.must_equal ['DROP INDEX "tnv2" CASCADE']
631
- end
632
- end
633
-
634
- it "should support dropping indexes concurrently" do
635
- @db.add_index :test, [:name, :value], :name=>'tnv2'
636
- @db.sqls.clear
637
- @db.drop_index :test, [:name, :value], :concurrently=>true, :name=>'tnv2'
638
- check_sqls do
639
- @db.sqls.must_equal ['DROP INDEX CONCURRENTLY "tnv2"']
640
- end
641
- end if DB.server_version >= 90200
642
-
643
- it "#lock should lock table if inside a transaction" do
644
- @db.transaction{@d.lock('EXCLUSIVE'); @d.insert(:name=>'a')}
645
- end
646
-
647
- it "#lock should return nil" do
648
- @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}.must_equal nil
649
- @db.transaction{@d.lock('EXCLUSIVE').must_equal nil; @d.insert(:name=>'a')}
650
- end
651
-
652
- it "should raise an error if attempting to update a joined dataset with a single FROM table" do
653
- proc{@db[:test].join(:test, [:name]).update(:name=>'a')}.must_raise(Sequel::Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs')
654
- end
655
-
656
- it "should truncate with options" do
657
- @d << { :name => 'abc', :value => 1}
658
- @d.count.must_equal 1
659
- @d.truncate(:cascade => true)
660
- @d.count.must_equal 0
661
- if @d.db.server_version > 80400
662
- @d << { :name => 'abc', :value => 1}
663
- @d.truncate(:cascade => true, :only=>true, :restart=>true)
664
- @d.count.must_equal 0
665
- end
666
- end
667
-
668
- it "should truncate multiple tables at once" do
669
- tables = [:test, :test]
670
- tables.each{|t| @d.from(t).insert}
671
- @d.from(:test, :test).truncate
672
- tables.each{|t| @d.from(t).count.must_equal 0}
673
- end
674
- end
675
-
676
- describe "Dataset#distinct" do
677
- before do
678
- @db = DB
679
- @db.create_table!(:a) do
680
- Integer :a
681
- Integer :b
682
- end
683
- @ds = @db[:a]
684
- end
685
- after do
686
- @db.drop_table?(:a)
687
- end
688
-
689
- it "#distinct with arguments should return results distinct on those arguments" do
690
- @ds.insert(20, 10)
691
- @ds.insert(30, 10)
692
- @ds.order(:b, :a).distinct.map(:a).must_equal [20, 30]
693
- @ds.order(:b, Sequel.desc(:a)).distinct.map(:a).must_equal [30, 20]
694
- @ds.order(:b, :a).distinct(:b).map(:a).must_equal [20]
695
- @ds.order(:b, Sequel.desc(:a)).distinct(:b).map(:a).must_equal [30]
696
- end
697
- end
698
-
699
- if DB.pool.respond_to?(:max_size) and DB.pool.max_size > 1
700
- describe "Dataset#for_update support" do
701
- before do
702
- @db = DB.create_table!(:items) do
703
- primary_key :id
704
- Integer :number
705
- String :name
706
- end
707
- @ds = DB[:items]
708
- end
709
- after do
710
- DB.drop_table?(:items)
711
- DB.disconnect
712
- end
713
-
714
- it "should handle FOR UPDATE" do
715
- @ds.insert(:number=>20)
716
- c, t = nil, nil
717
- q = Queue.new
718
- DB.transaction do
719
- @ds.for_update.first(:id=>1)
720
- t = Thread.new do
721
- DB.transaction do
722
- q.push nil
723
- @ds.filter(:id=>1).update(:name=>'Jim')
724
- c = @ds.first(:id=>1)
725
- q.push nil
726
- end
727
- end
728
- q.pop
729
- @ds.filter(:id=>1).update(:number=>30)
730
- end
731
- q.pop
732
- t.join
733
- c.must_equal(:id=>1, :number=>30, :name=>'Jim')
734
- end
735
-
736
- it "should handle FOR SHARE" do
737
- @ds.insert(:number=>20)
738
- c, t = nil
739
- q = Queue.new
740
- DB.transaction do
741
- @ds.for_share.first(:id=>1)
742
- t = Thread.new do
743
- DB.transaction do
744
- c = @ds.for_share.filter(:id=>1).first
745
- q.push nil
746
- end
747
- end
748
- q.pop
749
- @ds.filter(:id=>1).update(:name=>'Jim')
750
- c.must_equal(:id=>1, :number=>20, :name=>nil)
751
- end
752
- t.join
753
- end
754
- end
755
- end
756
-
757
- describe "A PostgreSQL dataset with a timestamp field" do
758
- before(:all) do
759
- @db = DB
760
- @db.create_table! :test3 do
761
- Date :date
762
- DateTime :time
763
- end
764
- @d = @db[:test3]
765
- end
766
- before do
767
- @d.delete
768
- end
769
- after do
770
- @db.convert_infinite_timestamps = false if @db.adapter_scheme == :postgres
771
- end
772
- after(:all) do
773
- @db.drop_table?(:test3)
774
- end
775
-
776
- cspecify "should store milliseconds in time fields for Time objects", [:do], [:swift] do
777
- t = Time.now
778
- @d << {:time=>t}
779
- t2 = @d.get(:time)
780
- @d.literal(t2).must_equal @d.literal(t)
781
- t2.strftime('%Y-%m-%d %H:%M:%S').must_equal t.strftime('%Y-%m-%d %H:%M:%S')
782
- (t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).must_equal t.usec
783
- end
784
-
785
- cspecify "should store milliseconds in time fields for DateTime objects", [:do], [:swift] do
786
- t = DateTime.now
787
- @d << {:time=>t}
788
- t2 = @d.get(:time)
789
- @d.literal(t2).must_equal @d.literal(t)
790
- t2.strftime('%Y-%m-%d %H:%M:%S').must_equal t.strftime('%Y-%m-%d %H:%M:%S')
791
- (t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).must_equal t.strftime('%N').to_i/1000
792
- end
793
-
794
- if DB.adapter_scheme == :postgres
795
- it "should handle infinite timestamps if convert_infinite_timestamps is set" do
796
- @d << {:time=>Sequel.cast('infinity', DateTime)}
797
- @db.convert_infinite_timestamps = :nil
798
- @db[:test3].get(:time).must_equal nil
799
- @db.convert_infinite_timestamps = :string
800
- @db[:test3].get(:time).must_equal 'infinity'
801
- @db.convert_infinite_timestamps = :float
802
- @db[:test3].get(:time).must_equal 1.0/0.0
803
- @db.convert_infinite_timestamps = 'nil'
804
- @db[:test3].get(:time).must_equal nil
805
- @db.convert_infinite_timestamps = 'string'
806
- @db[:test3].get(:time).must_equal 'infinity'
807
- @db.convert_infinite_timestamps = 'float'
808
- @db[:test3].get(:time).must_equal 1.0/0.0
809
- @db.convert_infinite_timestamps = 't'
810
- @db[:test3].get(:time).must_equal 1.0/0.0
811
- if ((Time.parse('infinity'); nil) rescue true)
812
- # Skip for loose time parsing (e.g. old rbx)
813
- @db.convert_infinite_timestamps = 'f'
814
- proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
815
- @db.convert_infinite_timestamps = nil
816
- proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
817
- @db.convert_infinite_timestamps = false
818
- proc{@db[:test3].get(:time)}.must_raise ArgumentError, Sequel::InvalidValue
819
- end
820
-
821
- @d.update(:time=>Sequel.cast('-infinity', DateTime))
822
- @db.convert_infinite_timestamps = :nil
823
- @db[:test3].get(:time).must_equal nil
824
- @db.convert_infinite_timestamps = :string
825
- @db[:test3].get(:time).must_equal '-infinity'
826
- @db.convert_infinite_timestamps = :float
827
- @db[:test3].get(:time).must_equal(-1.0/0.0)
828
- end
829
-
830
- it "should handle conversions from infinite strings/floats in models" do
831
- c = Class.new(Sequel::Model(:test3))
832
- @db.convert_infinite_timestamps = :float
833
- c.new(:time=>'infinity').time.must_equal 'infinity'
834
- c.new(:time=>'-infinity').time.must_equal '-infinity'
835
- c.new(:time=>1.0/0.0).time.must_equal 1.0/0.0
836
- c.new(:time=>-1.0/0.0).time.must_equal(-1.0/0.0)
837
- end
838
-
839
- it "should handle infinite dates if convert_infinite_timestamps is set" do
840
- @d << {:date=>Sequel.cast('infinity', Date)}
841
- @db.convert_infinite_timestamps = :nil
842
- @db[:test3].get(:date).must_equal nil
843
- @db.convert_infinite_timestamps = :string
844
- @db[:test3].get(:date).must_equal 'infinity'
845
- @db.convert_infinite_timestamps = :float
846
- @db[:test3].get(:date).must_equal 1.0/0.0
847
-
848
- @d.update(:date=>Sequel.cast('-infinity', :timestamp))
849
- @db.convert_infinite_timestamps = :nil
850
- @db[:test3].get(:date).must_equal nil
851
- @db.convert_infinite_timestamps = :string
852
- @db[:test3].get(:date).must_equal '-infinity'
853
- @db.convert_infinite_timestamps = :float
854
- @db[:test3].get(:date).must_equal(-1.0/0.0)
855
- end
856
-
857
- it "should handle conversions from infinite strings/floats in models" do
858
- c = Class.new(Sequel::Model(:test3))
859
- @db.convert_infinite_timestamps = :float
860
- c.new(:date=>'infinity').date.must_equal 'infinity'
861
- c.new(:date=>'-infinity').date.must_equal '-infinity'
862
- c.new(:date=>1.0/0.0).date.must_equal 1.0/0.0
863
- c.new(:date=>-1.0/0.0).date.must_equal(-1.0/0.0)
864
- end
865
- end
866
-
867
- it "explain and analyze should not raise errors" do
868
- @d = DB[:test3]
869
- @d.explain
870
- @d.analyze
871
- end
872
-
873
- it "#locks should be a dataset returning database locks " do
874
- @db.locks.must_be_kind_of(Sequel::Dataset)
875
- @db.locks.all.must_be_kind_of(Array)
876
- end
877
- end
878
-
879
- describe "A PostgreSQL database" do
880
- before do
881
- @db = DB
882
- @db.create_table! :test2 do
883
- text :name
884
- integer :value
885
- end
886
- end
887
- after do
888
- @db.drop_table?(:test2)
889
- end
890
-
891
- it "should support column operations" do
892
- @db.create_table!(:test2){text :name; integer :value}
893
- @db[:test2] << {}
894
- @db[:test2].columns.must_equal [:name, :value]
895
-
896
- @db.add_column :test2, :xyz, :text, :default => '000'
897
- @db[:test2].columns.must_equal [:name, :value, :xyz]
898
- @db[:test2] << {:name => 'mmm', :value => 111}
899
- @db[:test2].first[:xyz].must_equal '000'
900
-
901
- @db[:test2].columns.must_equal [:name, :value, :xyz]
902
- @db.drop_column :test2, :xyz
903
-
904
- @db[:test2].columns.must_equal [:name, :value]
905
-
906
- @db[:test2].delete
907
- @db.add_column :test2, :xyz, :text, :default => '000'
908
- @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
909
-
910
- @db[:test2].columns.must_equal [:name, :value, :xyz]
911
- @db.rename_column :test2, :xyz, :zyx
912
- @db[:test2].columns.must_equal [:name, :value, :zyx]
913
- @db[:test2].first[:zyx].must_equal 'qqqq'
914
-
915
- @db.add_column :test2, :xyz, :float
916
- @db[:test2].delete
917
- @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
918
- @db.set_column_type :test2, :xyz, :integer
919
-
920
- @db[:test2].first[:xyz].must_equal 57
921
- end
922
- end
923
-
924
- describe "A PostgreSQL database" do
925
- before do
926
- @db = DB
927
- @db.drop_table?(:posts)
928
- @db.sqls.clear
929
- end
930
- after do
931
- @db.drop_table?(:posts)
932
- end
933
-
934
- it "should support resetting the primary key sequence" do
935
- @db.create_table(:posts){primary_key :a}
936
- @db[:posts].insert(:a=>20).must_equal 20
937
- @db[:posts].insert.must_equal 1
938
- @db[:posts].insert.must_equal 2
939
- @db[:posts].insert(:a=>10).must_equal 10
940
- @db.reset_primary_key_sequence(:posts).must_equal 21
941
- @db[:posts].insert.must_equal 21
942
- @db[:posts].order(:a).map(:a).must_equal [1, 2, 10, 20, 21]
943
- end
944
-
945
- it "should support specifying Integer/Bignum/Fixnum types in primary keys and have them be auto incrementing" do
946
- @db.create_table(:posts){primary_key :a, :type=>Integer}
947
- @db[:posts].insert.must_equal 1
948
- @db[:posts].insert.must_equal 2
949
- @db.create_table!(:posts){primary_key :a, :type=>Fixnum}
950
- @db[:posts].insert.must_equal 1
951
- @db[:posts].insert.must_equal 2
952
- @db.create_table!(:posts){primary_key :a, :type=>:Bignum}
953
- @db[:posts].insert.must_equal 1
954
- @db[:posts].insert.must_equal 2
955
- end
956
-
957
- it "should not raise an error if attempting to resetting the primary key sequence for a table without a primary key" do
958
- @db.create_table(:posts){Integer :a}
959
- @db.reset_primary_key_sequence(:posts).must_equal nil
960
- end
961
-
962
- it "should support opclass specification" do
963
- @db.create_table(:posts){text :title; text :body; integer :user_id; index(:user_id, :opclass => :int4_ops, :type => :btree)}
964
- check_sqls do
965
- @db.sqls.must_equal [
966
- 'CREATE TABLE "posts" ("title" text, "body" text, "user_id" integer)',
967
- 'CREATE INDEX "posts_user_id_index" ON "posts" USING btree ("user_id" int4_ops)'
968
- ]
969
- end
970
- end
971
-
972
- it "should support fulltext indexes and searching" do
973
- @db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french', :index_type=>:gist}
974
-
975
- @db[:posts].insert(:title=>'ruby rails', :body=>'yowsa')
976
- @db[:posts].insert(:title=>'sequel', :body=>'ruby')
977
- @db[:posts].insert(:title=>'ruby scooby', :body=>'x')
978
-
979
- @db[:posts].full_text_search(:title, 'rails').all.must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
980
- @db[:posts].full_text_search(:title, 'rails', :headline=>true).all.must_equal [{:title=>'ruby rails', :body=>'yowsa', :headline=>'ruby <b>rails</b>'}]
981
- @db[:posts].full_text_search([:title, :body], ['yowsa', 'rails']).all.must_equal [:title=>'ruby rails', :body=>'yowsa']
982
- @db[:posts].full_text_search(:title, 'scooby', :language => 'french').all.must_equal [{:title=>'ruby scooby', :body=>'x'}]
983
-
984
- @db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
985
- @db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
986
-
987
- @db[:posts].insert(:title=>'jruby rubinius ruby maglev mri iron')
988
- @db[:posts].insert(:title=>'ruby jruby maglev mri rubinius iron')
989
- @db[:posts].full_text_search(:title, 'rubinius ruby', :phrase=>true).select_order_map(:title).must_equal ['jruby rubinius ruby maglev mri iron']
990
- @db[:posts].full_text_search(:title, 'jruby maglev', :phrase=>true).select_order_map(:title).must_equal ['ruby jruby maglev mri rubinius iron']
991
- @db[:posts].full_text_search(:title, 'rubinius ruby', :plain=>true).select_order_map(:title).must_equal ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
992
- @db[:posts].full_text_search(:title, 'jruby maglev', :plain=>true).select_order_map(:title).must_equal ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
993
-
994
- @db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rails', :tsvector=>true).all.must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
995
- @db[:posts].full_text_search(:title, Sequel.function(:to_tsquery, 'simple', 'rails'), :tsquery=>true).all.must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
996
- proc{@db[:posts].full_text_search(Sequel.function(:to_tsvector, 'simple', :title), 'rubinius ruby', :tsvector=>true, :phrase=>true)}.must_raise(Sequel::Error)
997
- proc{@db[:posts].full_text_search(:title, Sequel.function(:to_tsquery, 'simple', 'rails'), :tsquery=>true, :phrase=>true)}.must_raise(Sequel::Error)
998
-
999
- @db[:posts].delete
1000
- t1 = "bork " * 1000 + "ruby sequel"
1001
- t2 = "ruby sequel " * 1000
1002
- @db[:posts].insert(:title=>t1)
1003
- @db[:posts].insert(:title=>t2)
1004
- @db[:posts].full_text_search(:title, 'ruby & sequel', :rank=>true).select_map(:title).must_equal [t2, t1]
1005
- end if DB.server_version >= 80300
1006
-
1007
- it "should support spatial indexes" do
1008
- @db.create_table(:posts){box :geom; spatial_index [:geom]}
1009
- check_sqls do
1010
- @db.sqls.must_equal [
1011
- 'CREATE TABLE "posts" ("geom" box)',
1012
- 'CREATE INDEX "posts_geom_index" ON "posts" USING gist ("geom")'
1013
- ]
1014
- end
1015
- end
1016
-
1017
- it "should support indexes with index type" do
1018
- @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'hash'}
1019
- check_sqls do
1020
- @db.sqls.must_equal [
1021
- 'CREATE TABLE "posts" ("title" varchar(5))',
1022
- 'CREATE INDEX "posts_title_index" ON "posts" USING hash ("title")'
1023
- ]
1024
- end
1025
- end
1026
-
1027
- it "should support unique indexes with index type" do
1028
- @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'btree', :unique => true}
1029
- check_sqls do
1030
- @db.sqls.must_equal [
1031
- 'CREATE TABLE "posts" ("title" varchar(5))',
1032
- 'CREATE UNIQUE INDEX "posts_title_index" ON "posts" USING btree ("title")'
1033
- ]
1034
- end
1035
- end
1036
-
1037
- it "should support partial indexes" do
1038
- @db.create_table(:posts){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
1039
- check_sqls do
1040
- @db.sqls.must_equal [
1041
- 'CREATE TABLE "posts" ("title" varchar(5))',
1042
- 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
1043
- ]
1044
- end
1045
- end
1046
-
1047
- it "should support identifiers for table names in indicies" do
1048
- @db.create_table(Sequel::SQL::Identifier.new(:posts)){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
1049
- check_sqls do
1050
- @db.sqls.must_equal [
1051
- 'CREATE TABLE "posts" ("title" varchar(5))',
1052
- 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
1053
- ]
1054
- end
1055
- end
1056
-
1057
- it "should support renaming tables" do
1058
- @db.create_table!(:posts1){primary_key :a}
1059
- @db.rename_table(:posts1, :posts)
1060
- end
1061
- end
1062
-
1063
- describe "Postgres::Dataset#import" do
1064
- before do
1065
- @db = DB
1066
- @db.create_table!(:test){primary_key :x; Integer :y}
1067
- @db.sqls.clear
1068
- @ds = @db[:test]
1069
- end
1070
- after do
1071
- @db.drop_table?(:test)
1072
- end
1073
-
1074
-
1075
- it "#import should a single insert statement" do
1076
- @ds.import([:x, :y], [[1, 2], [3, 4]])
1077
- check_sqls do
1078
- @db.sqls.must_equal ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)', 'COMMIT']
1079
- end
1080
- @ds.all.must_equal [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
1081
- end
1082
-
1083
- it "#import should work correctly when returning primary keys" do
1084
- @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).must_equal [1, 3]
1085
- @ds.all.must_equal [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
1086
- end
1087
-
1088
- it "#import should work correctly when returning primary keys with :slice option" do
1089
- @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).must_equal [1, 3]
1090
- @ds.all.must_equal [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
1091
- end
1092
-
1093
- it "#import should work correctly with an arbitrary returning value" do
1094
- @ds.returning(:y, :x).import([:x, :y], [[1, 2], [3, 4]]).must_equal [{:y=>2, :x=>1}, {:y=>4, :x=>3}]
1095
- @ds.all.must_equal [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
1096
- end
1097
- end
1098
-
1099
- describe "Postgres::Dataset#insert" do
1100
- before do
1101
- @db = DB
1102
- @db.create_table!(:test5){primary_key :xid; Integer :value}
1103
- @db.sqls.clear
1104
- @ds = @db[:test5]
1105
- end
1106
- after do
1107
- @db.drop_table?(:test5)
1108
- end
1109
-
1110
- it "should work with static SQL" do
1111
- @ds.with_sql('INSERT INTO test5 (value) VALUES (10)').insert.must_equal nil
1112
- @db['INSERT INTO test5 (value) VALUES (20)'].insert.must_equal nil
1113
- @ds.all.must_equal [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}]
1114
- end
1115
-
1116
- it "should insert correctly if using a column array and a value array" do
1117
- @ds.insert([:value], [10]).must_equal 1
1118
- @ds.all.must_equal [{:xid=>1, :value=>10}]
1119
- end
1120
-
1121
- it "should use INSERT RETURNING" do
1122
- @ds.insert(:value=>10).must_equal 1
1123
- check_sqls do
1124
- @db.sqls.last.must_equal 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"'
1125
- end
1126
- end
1127
-
1128
- it "should have insert_select insert the record and return the inserted record" do
1129
- h = @ds.insert_select(:value=>10)
1130
- h[:value].must_equal 10
1131
- @ds.first(:xid=>h[:xid])[:value].must_equal 10
1132
- end
1133
-
1134
- it "should have insert_select respect existing returning clause" do
1135
- h = @ds.returning(:value___v, :xid___x).insert_select(:value=>10)
1136
- h[:v].must_equal 10
1137
- @ds.first(:xid=>h[:x])[:value].must_equal 10
1138
- end
1139
-
1140
- it "should have prepared insert_select respect existing returning clause" do
1141
- h = @ds.returning(:value___v, :xid___x).prepare(:insert_select, :insert_select, :value=>10).call
1142
- h[:v].must_equal 10
1143
- @ds.first(:xid=>h[:x])[:value].must_equal 10
1144
- end
1145
-
1146
- it "should correctly return the inserted record's primary key value" do
1147
- value1 = 10
1148
- id1 = @ds.insert(:value=>value1)
1149
- @ds.first(:xid=>id1)[:value].must_equal value1
1150
- value2 = 20
1151
- id2 = @ds.insert(:value=>value2)
1152
- @ds.first(:xid=>id2)[:value].must_equal value2
1153
- end
1154
-
1155
- it "should return nil if the table has no primary key" do
1156
- @db.create_table!(:test5){String :name; Integer :value}
1157
- @ds.delete
1158
- @ds.insert(:name=>'a').must_equal nil
1159
- end
1160
- end
1161
-
1162
- describe "Postgres::Database schema qualified tables" do
1163
- before do
1164
- @db = DB
1165
- @db << "CREATE SCHEMA schema_test"
1166
- @db.instance_variable_set(:@primary_keys, {})
1167
- @db.instance_variable_set(:@primary_key_sequences, {})
1168
- end
1169
- after do
1170
- @db << "DROP SCHEMA schema_test CASCADE"
1171
- end
1172
-
1173
- it "should be able to create, drop, select and insert into tables in a given schema" do
1174
- @db.create_table(:schema_test__schema_test){primary_key :i}
1175
- @db[:schema_test__schema_test].first.must_equal nil
1176
- @db[:schema_test__schema_test].insert(:i=>1).must_equal 1
1177
- @db[:schema_test__schema_test].first.must_equal(:i=>1)
1178
- @db.from(Sequel.lit('schema_test.schema_test')).first.must_equal(:i=>1)
1179
- @db.drop_table(:schema_test__schema_test)
1180
- @db.create_table(Sequel.qualify(:schema_test, :schema_test)){integer :i}
1181
- @db[:schema_test__schema_test].first.must_equal nil
1182
- @db.from(Sequel.lit('schema_test.schema_test')).first.must_equal nil
1183
- @db.drop_table(Sequel.qualify(:schema_test, :schema_test))
1184
- end
1185
-
1186
- it "#tables should not include tables in a default non-public schema" do
1187
- @db.create_table(:schema_test__schema_test){integer :i}
1188
- @db.tables(:schema=>:schema_test).must_include(:schema_test)
1189
- @db.tables.wont_include(:pg_am)
1190
- @db.tables.wont_include(:domain_udt_usage)
1191
- end
1192
-
1193
- it "#tables should return tables in the schema provided by the :schema argument" do
1194
- @db.create_table(:schema_test__schema_test){integer :i}
1195
- @db.tables(:schema=>:schema_test).must_equal [:schema_test]
1196
- end
1197
-
1198
- it "#schema should not include columns from tables in a default non-public schema" do
1199
- @db.create_table(:schema_test__domains){integer :i}
1200
- sch = @db.schema(:schema_test__domains)
1201
- cs = sch.map{|x| x.first}
1202
- cs.must_include(:i)
1203
- cs.wont_include(:data_type)
1204
- end
1205
-
1206
- it "#schema should only include columns from the table in the given :schema argument" do
1207
- @db.create_table!(:domains){integer :d}
1208
- @db.create_table(:schema_test__domains){integer :i}
1209
- sch = @db.schema(:domains, :schema=>:schema_test)
1210
- cs = sch.map{|x| x.first}
1211
- cs.must_include(:i)
1212
- cs.wont_include(:d)
1213
- @db.drop_table(:domains)
1214
- end
1215
-
1216
- it "#schema should not include columns in tables from other domains by default" do
1217
- @db.create_table!(:public__domains){integer :d}
1218
- @db.create_table(:schema_test__domains){integer :i}
1219
- begin
1220
- @db.schema(:domains).map{|x| x.first}.must_equal [:d]
1221
- @db.schema(:schema_test__domains).map{|x| x.first}.must_equal [:i]
1222
- ensure
1223
- @db.drop_table?(:public__domains)
1224
- end
1225
- end
1226
-
1227
- it "#table_exists? should see if the table is in a given schema" do
1228
- @db.create_table(:schema_test__schema_test){integer :i}
1229
- @db.table_exists?(:schema_test__schema_test).must_equal true
1230
- end
1231
-
1232
- it "should be able to add and drop indexes in a schema" do
1233
- @db.create_table(:schema_test__schema_test){Integer :i, :index=>true}
1234
- @db.indexes(:schema_test__schema_test).keys.must_equal [:schema_test_schema_test_i_index]
1235
- @db.drop_index :schema_test__schema_test, :i
1236
- @db.indexes(:schema_test__schema_test).keys.must_equal []
1237
- end
1238
-
1239
- it "should be able to get primary keys for tables in a given schema" do
1240
- @db.create_table(:schema_test__schema_test){primary_key :i}
1241
- @db.primary_key(:schema_test__schema_test).must_equal 'i'
1242
- end
1243
-
1244
- it "should be able to get serial sequences for tables in a given schema" do
1245
- @db.create_table(:schema_test__schema_test){primary_key :i}
1246
- @db.primary_key_sequence(:schema_test__schema_test).must_equal '"schema_test"."schema_test_i_seq"'
1247
- end
1248
-
1249
- it "should be able to get serial sequences for tables that have spaces in the name in a given schema" do
1250
- @db.create_table(:"schema_test__schema test"){primary_key :i}
1251
- @db.primary_key_sequence(:"schema_test__schema test").must_equal '"schema_test"."schema test_i_seq"'
1252
- end
1253
-
1254
- it "should be able to get custom sequences for tables in a given schema" do
1255
- @db << "CREATE SEQUENCE schema_test.kseq"
1256
- @db.create_table(:schema_test__schema_test){integer :j; primary_key :k, :type=>:integer, :default=>Sequel.lit("nextval('schema_test.kseq'::regclass)")}
1257
- @db.primary_key_sequence(:schema_test__schema_test).must_equal '"schema_test".kseq'
1258
- end
1259
-
1260
- it "should be able to get custom sequences for tables that have spaces in the name in a given schema" do
1261
- @db << "CREATE SEQUENCE schema_test.\"ks eq\""
1262
- @db.create_table(:"schema_test__schema test"){integer :j; primary_key :k, :type=>:integer, :default=>Sequel.lit("nextval('schema_test.\"ks eq\"'::regclass)")}
1263
- @db.primary_key_sequence(:"schema_test__schema test").must_equal '"schema_test"."ks eq"'
1264
- end
1265
-
1266
- it "should handle schema introspection cases with tables with same name in multiple schemas" do
1267
- begin
1268
- @db.create_table(:schema_test__schema_test) do
1269
- primary_key :id
1270
- foreign_key :i, :schema_test__schema_test, :index=>{:name=>:schema_test_sti}
1271
- end
1272
- @db.create_table!(:public__schema_test) do
1273
- primary_key :id
1274
- foreign_key :j, :public__schema_test, :index=>{:name=>:public_test_sti}
1275
- end
1276
-
1277
- h = @db.schema(:schema_test)
1278
- h.length.must_equal 2
1279
- h.last.first.must_equal :j
1280
-
1281
- @db.indexes(:schema_test).must_equal(:public_test_sti=>{:unique=>false, :columns=>[:j], :deferrable=>nil})
1282
- @db.foreign_key_list(:schema_test).must_equal [{:on_update=>:no_action, :columns=>[:j], :deferrable=>false, :key=>[:id], :table=>:schema_test, :on_delete=>:no_action, :name=>:schema_test_j_fkey}]
1283
- ensure
1284
- @db.drop_table?(:public__schema_test)
1285
- end
1286
- end
1287
- end
1288
-
1289
- describe "Postgres::Database schema qualified tables and eager graphing" do
1290
- before(:all) do
1291
- @db = DB
1292
- @db.run "DROP SCHEMA s CASCADE" rescue nil
1293
- @db.run "CREATE SCHEMA s"
1294
-
1295
- @db.create_table(:s__bands){primary_key :id; String :name}
1296
- @db.create_table(:s__albums){primary_key :id; String :name; foreign_key :band_id, :s__bands}
1297
- @db.create_table(:s__tracks){primary_key :id; String :name; foreign_key :album_id, :s__albums}
1298
- @db.create_table(:s__members){primary_key :id; String :name; foreign_key :band_id, :s__bands}
1299
-
1300
- @Band = Class.new(Sequel::Model(:s__bands))
1301
- @Album = Class.new(Sequel::Model(:s__albums))
1302
- @Track = Class.new(Sequel::Model(:s__tracks))
1303
- @Member = Class.new(Sequel::Model(:s__members))
1304
- def @Band.name; :Band; end
1305
- def @Album.name; :Album; end
1306
- def @Track.name; :Track; end
1307
- def @Member.name; :Member; end
1308
-
1309
- @Band.one_to_many :albums, :class=>@Album, :order=>:name
1310
- @Band.one_to_many :members, :class=>@Member, :order=>:name
1311
- @Album.many_to_one :band, :class=>@Band, :order=>:name
1312
- @Album.one_to_many :tracks, :class=>@Track, :order=>:name
1313
- @Track.many_to_one :album, :class=>@Album, :order=>:name
1314
- @Member.many_to_one :band, :class=>@Band, :order=>:name
1315
-
1316
- @Member.many_to_many :members, :class=>@Member, :join_table=>:s__bands, :right_key=>:id, :left_key=>:id, :left_primary_key=>:band_id, :right_primary_key=>:band_id, :order=>:name
1317
- @Band.many_to_many :tracks, :class=>@Track, :join_table=>:s__albums, :right_key=>:id, :right_primary_key=>:album_id, :order=>:name
1318
-
1319
- @b1 = @Band.create(:name=>"BM")
1320
- @b2 = @Band.create(:name=>"J")
1321
- @a1 = @Album.create(:name=>"BM1", :band=>@b1)
1322
- @a2 = @Album.create(:name=>"BM2", :band=>@b1)
1323
- @a3 = @Album.create(:name=>"GH", :band=>@b2)
1324
- @a4 = @Album.create(:name=>"GHL", :band=>@b2)
1325
- @t1 = @Track.create(:name=>"BM1-1", :album=>@a1)
1326
- @t2 = @Track.create(:name=>"BM1-2", :album=>@a1)
1327
- @t3 = @Track.create(:name=>"BM2-1", :album=>@a2)
1328
- @t4 = @Track.create(:name=>"BM2-2", :album=>@a2)
1329
- @m1 = @Member.create(:name=>"NU", :band=>@b1)
1330
- @m2 = @Member.create(:name=>"TS", :band=>@b1)
1331
- @m3 = @Member.create(:name=>"NS", :band=>@b2)
1332
- @m4 = @Member.create(:name=>"JC", :band=>@b2)
1333
- end
1334
- after(:all) do
1335
- @db.run "DROP SCHEMA s CASCADE"
1336
- end
1337
-
1338
- it "should return all eager graphs correctly" do
1339
- bands = @Band.order(:bands__name).eager_graph(:albums).all
1340
- bands.must_equal [@b1, @b2]
1341
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1342
-
1343
- bands = @Band.order(:bands__name).eager_graph(:albums=>:tracks).all
1344
- bands.must_equal [@b1, @b2]
1345
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1346
- bands.map{|x| x.albums.map{|y| y.tracks}}.must_equal [[[@t1, @t2], [@t3, @t4]], [[], []]]
1347
-
1348
- bands = @Band.order(:bands__name).eager_graph({:albums=>:tracks}, :members).all
1349
- bands.must_equal [@b1, @b2]
1350
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1351
- bands.map{|x| x.albums.map{|y| y.tracks}}.must_equal [[[@t1, @t2], [@t3, @t4]], [[], []]]
1352
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1353
- end
1354
-
1355
- it "should have eager graphs work with previous joins" do
1356
- bands = @Band.order(:bands__name).select_all(:s__bands).join(:s__members, :band_id=>:id).from_self(:alias=>:bands0).eager_graph(:albums=>:tracks).all
1357
- bands.must_equal [@b1, @b2]
1358
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1359
- bands.map{|x| x.albums.map{|y| y.tracks}}.must_equal [[[@t1, @t2], [@t3, @t4]], [[], []]]
1360
- end
1361
-
1362
- it "should have eager graphs work with joins with the same tables" do
1363
- bands = @Band.order(:bands__name).select_all(:s__bands).join(:s__members, :band_id=>:id).eager_graph({:albums=>:tracks}, :members).all
1364
- bands.must_equal [@b1, @b2]
1365
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1366
- bands.map{|x| x.albums.map{|y| y.tracks}}.must_equal [[[@t1, @t2], [@t3, @t4]], [[], []]]
1367
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1368
- end
1369
-
1370
- it "should have eager graphs work with self referential associations" do
1371
- bands = @Band.order(:bands__name).eager_graph(:tracks=>{:album=>:band}).all
1372
- bands.must_equal [@b1, @b2]
1373
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1374
- bands.map{|x| x.tracks.map{|y| y.album}}.must_equal [[@a1, @a1, @a2, @a2], []]
1375
- bands.map{|x| x.tracks.map{|y| y.album.band}}.must_equal [[@b1, @b1, @b1, @b1], []]
1376
-
1377
- members = @Member.order(:members__name).eager_graph(:members).all
1378
- members.must_equal [@m4, @m3, @m1, @m2]
1379
- members.map{|x| x.members}.must_equal [[@m4, @m3], [@m4, @m3], [@m1, @m2], [@m1, @m2]]
1380
-
1381
- members = @Member.order(:members__name).eager_graph(:band, :members=>:band).all
1382
- members.must_equal [@m4, @m3, @m1, @m2]
1383
- members.map{|x| x.band}.must_equal [@b2, @b2, @b1, @b1]
1384
- members.map{|x| x.members}.must_equal [[@m4, @m3], [@m4, @m3], [@m1, @m2], [@m1, @m2]]
1385
- members.map{|x| x.members.map{|y| y.band}}.must_equal [[@b2, @b2], [@b2, @b2], [@b1, @b1], [@b1, @b1]]
1386
- end
1387
-
1388
- it "should have eager graphs work with a from_self dataset" do
1389
- bands = @Band.order(:bands__name).from_self.eager_graph(:tracks=>{:album=>:band}).all
1390
- bands.must_equal [@b1, @b2]
1391
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1392
- bands.map{|x| x.tracks.map{|y| y.album}}.must_equal [[@a1, @a1, @a2, @a2], []]
1393
- bands.map{|x| x.tracks.map{|y| y.album.band}}.must_equal [[@b1, @b1, @b1, @b1], []]
1394
- end
1395
-
1396
- it "should have eager graphs work with different types of aliased from tables" do
1397
- bands = @Band.order(:tracks__name).from(:s__bands___tracks).eager_graph(:tracks).all
1398
- bands.must_equal [@b1, @b2]
1399
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1400
-
1401
- bands = @Band.order(:tracks__name).from(Sequel.expr(:s__bands).as(:tracks)).eager_graph(:tracks).all
1402
- bands.must_equal [@b1, @b2]
1403
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1404
-
1405
- bands = @Band.order(:tracks__name).from(Sequel.expr(:s__bands).as(Sequel.identifier(:tracks))).eager_graph(:tracks).all
1406
- bands.must_equal [@b1, @b2]
1407
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1408
-
1409
- bands = @Band.order(:tracks__name).from(Sequel.expr(:s__bands).as('tracks')).eager_graph(:tracks).all
1410
- bands.must_equal [@b1, @b2]
1411
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1412
- end
1413
-
1414
- it "should have eager graphs work with join tables with aliases" do
1415
- bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums___tracks, :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
1416
- bands.must_equal [@b1, @b2]
1417
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1418
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1419
-
1420
- bands = @Band.order(:bands__name).eager_graph(:members).join(Sequel.as(:s__albums, :tracks), :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
1421
- bands.must_equal [@b1, @b2]
1422
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1423
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1424
-
1425
- bands = @Band.order(:bands__name).eager_graph(:members).join(Sequel.as(:s__albums, 'tracks'), :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
1426
- bands.must_equal [@b1, @b2]
1427
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1428
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1429
-
1430
- bands = @Band.order(:bands__name).eager_graph(:members).join(Sequel.as(:s__albums, Sequel.identifier(:tracks)), :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
1431
- bands.must_equal [@b1, @b2]
1432
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1433
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1434
-
1435
- bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>Sequel.qualify(:s__bands, :id)}, :table_alias=>:tracks).eager_graph(:albums=>:tracks).all
1436
- bands.must_equal [@b1, @b2]
1437
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1438
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1439
-
1440
- bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>Sequel.qualify(:s__bands, :id)}, :table_alias=>'tracks').eager_graph(:albums=>:tracks).all
1441
- bands.must_equal [@b1, @b2]
1442
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1443
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1444
-
1445
- bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>Sequel.qualify(:s__bands, :id)}, :table_alias=>Sequel.identifier(:tracks)).eager_graph(:albums=>:tracks).all
1446
- bands.must_equal [@b1, @b2]
1447
- bands.map{|x| x.albums}.must_equal [[@a1, @a2], [@a3, @a4]]
1448
- bands.map{|x| x.members}.must_equal [[@m1, @m2], [@m4, @m3]]
1449
- end
1450
-
1451
- it "should have eager graphs work with different types of qualified from tables" do
1452
- bands = @Band.order(:bands__name).from(Sequel.qualify(:s, :bands)).eager_graph(:tracks).all
1453
- bands.must_equal [@b1, @b2]
1454
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1455
-
1456
- bands = @Band.order(:bands__name).from(Sequel.identifier(:bands).qualify(:s)).eager_graph(:tracks).all
1457
- bands.must_equal [@b1, @b2]
1458
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1459
-
1460
- bands = @Band.order(:bands__name).from(Sequel::SQL::QualifiedIdentifier.new(:s, 'bands')).eager_graph(:tracks).all
1461
- bands.must_equal [@b1, @b2]
1462
- bands.map{|x| x.tracks}.must_equal [[@t1, @t2, @t3, @t4], []]
1463
- end
1464
-
1465
- end
1466
-
1467
- if DB.server_version >= 80300
1468
- describe "PostgreSQL tsearch2" do
1469
- before(:all) do
1470
- DB.create_table! :test6 do
1471
- text :title
1472
- text :body
1473
- full_text_index [:title, :body]
1474
- end
1475
- @ds = DB[:test6]
1476
- end
1477
- after do
1478
- DB[:test6].delete
1479
- end
1480
- after(:all) do
1481
- DB.drop_table?(:test6)
1482
- end
1483
-
1484
- it "should search by indexed column" do
1485
- record = {:title => "oopsla conference", :body => "test"}
1486
- @ds << record
1487
- @ds.full_text_search(:title, "oopsla").all.must_include(record)
1488
- end
1489
-
1490
- it "should join multiple coumns with spaces to search by last words in row" do
1491
- record = {:title => "multiple words", :body => "are easy to search"}
1492
- @ds << record
1493
- @ds.full_text_search([:title, :body], "words").all.must_include(record)
1494
- end
1495
-
1496
- it "should return rows with a NULL in one column if a match in another column" do
1497
- record = {:title => "multiple words", :body =>nil}
1498
- @ds << record
1499
- @ds.full_text_search([:title, :body], "words").all.must_include(record)
1500
- end
1501
- end
1502
- end
1503
-
1504
- if DB.dataset.supports_window_functions?
1505
- describe "Postgres::Dataset named windows" do
1506
- before do
1507
- @db = DB
1508
- @db.create_table!(:i1){Integer :id; Integer :group_id; Integer :amount}
1509
- @ds = @db[:i1].order(:id)
1510
- @ds.insert(:id=>1, :group_id=>1, :amount=>1)
1511
- @ds.insert(:id=>2, :group_id=>1, :amount=>10)
1512
- @ds.insert(:id=>3, :group_id=>1, :amount=>100)
1513
- @ds.insert(:id=>4, :group_id=>2, :amount=>1000)
1514
- @ds.insert(:id=>5, :group_id=>2, :amount=>10000)
1515
- @ds.insert(:id=>6, :group_id=>2, :amount=>100000)
1516
- end
1517
- after do
1518
- @db.drop_table?(:i1)
1519
- end
1520
-
1521
- it "should give correct results for window functions" do
1522
- @ds.window(:win, :partition=>:group_id, :order=>:id).select(:id){sum(:amount).over(:window=>win)}.all.
1523
- must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
1524
- @ds.window(:win, :partition=>:group_id).select(:id){sum(:amount).over(:window=>win, :order=>id)}.all.
1525
- must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
1526
- @ds.window(:win, {}).select(:id){sum(:amount).over(:window=>:win, :order=>id)}.all.
1527
- must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
1528
- @ds.window(:win, :partition=>:group_id).select(:id){sum(:amount).over(:window=>:win, :order=>id, :frame=>:all)}.all.
1529
- must_equal [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
1530
- end
1531
- end
1532
- end
1533
-
1534
- describe "Postgres::Database functions, languages, schemas, and triggers" do
1535
- before do
1536
- @d = DB
1537
- end
1538
- after do
1539
- @d.drop_function('tf', :if_exists=>true, :cascade=>true)
1540
- @d.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>%w'integer integer')
1541
- @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
1542
- @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
1543
- @d.drop_table?(:test)
1544
- end
1545
-
1546
- it "#create_function and #drop_function should create and drop functions" do
1547
- proc{@d['SELECT tf()'].all}.must_raise(Sequel::DatabaseError)
1548
- args = ['tf', 'SELECT 1', {:returns=>:integer}]
1549
- @d.send(:create_function_sql, *args).must_match(/\A\s*CREATE FUNCTION tf\(\)\s+RETURNS integer\s+LANGUAGE SQL\s+AS 'SELECT 1'\s*\z/)
1550
- @d.create_function(*args)
1551
- @d['SELECT tf()'].all.must_equal [{:tf=>1}]
1552
- @d.send(:drop_function_sql, 'tf').must_equal 'DROP FUNCTION tf()'
1553
- @d.drop_function('tf')
1554
- proc{@d['SELECT tf()'].all}.must_raise(Sequel::DatabaseError)
1555
- end
1556
-
1557
- it "#create_function and #drop_function should support options" do
1558
- args = ['tf', 'SELECT $1 + $2', {:args=>[[:integer, :a], :integer], :replace=>true, :returns=>:integer, :language=>'SQL', :behavior=>:immutable, :strict=>true, :security_definer=>true, :cost=>2, :set=>{:search_path => 'public'}}]
1559
- @d.send(:create_function_sql,*args).must_match(/\A\s*CREATE OR REPLACE FUNCTION tf\(a integer, integer\)\s+RETURNS integer\s+LANGUAGE SQL\s+IMMUTABLE\s+STRICT\s+SECURITY DEFINER\s+COST 2\s+SET search_path = public\s+AS 'SELECT \$1 \+ \$2'\s*\z/)
1560
- @d.create_function(*args)
1561
- # Make sure replace works
1562
- @d.create_function(*args)
1563
- @d['SELECT tf(1, 2)'].all.must_equal [{:tf=>3}]
1564
- args = ['tf', {:if_exists=>true, :cascade=>true, :args=>[[:integer, :a], :integer]}]
1565
- @d.send(:drop_function_sql,*args).must_equal 'DROP FUNCTION IF EXISTS tf(a integer, integer) CASCADE'
1566
- @d.drop_function(*args)
1567
- # Make sure if exists works
1568
- @d.drop_function(*args)
1569
- end
1570
-
1571
- it "#create_language and #drop_language should create and drop languages" do
1572
- @d.send(:create_language_sql, :plpgsql).must_equal 'CREATE LANGUAGE plpgsql'
1573
- @d.create_language(:plpgsql, :replace=>true) if @d.server_version < 90000
1574
- proc{@d.create_language(:plpgsql)}.must_raise(Sequel::DatabaseError)
1575
- @d.send(:drop_language_sql, :plpgsql).must_equal 'DROP LANGUAGE plpgsql'
1576
- @d.drop_language(:plpgsql) if @d.server_version < 90000
1577
- proc{@d.drop_language(:plpgsql)}.must_raise(Sequel::DatabaseError) if @d.server_version < 90000
1578
- @d.send(:create_language_sql, :plpgsql, :replace=>true, :trusted=>true, :handler=>:a, :validator=>:b).must_equal(@d.server_version >= 90000 ? 'CREATE OR REPLACE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b' : 'CREATE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b')
1579
- @d.send(:drop_language_sql, :plpgsql, :if_exists=>true, :cascade=>true).must_equal 'DROP LANGUAGE IF EXISTS plpgsql CASCADE'
1580
- # Make sure if exists works
1581
- @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
1582
- end
1583
-
1584
- it "#create_schema and #drop_schema should create and drop schemas" do
1585
- @d.send(:create_schema_sql, :sequel).must_equal 'CREATE SCHEMA "sequel"'
1586
- @d.send(:create_schema_sql, :sequel, :if_not_exists=>true, :owner=>:foo).must_equal 'CREATE SCHEMA IF NOT EXISTS "sequel" AUTHORIZATION "foo"'
1587
- @d.send(:drop_schema_sql, :sequel).must_equal 'DROP SCHEMA "sequel"'
1588
- @d.send(:drop_schema_sql, :sequel, :if_exists=>true, :cascade=>true).must_equal 'DROP SCHEMA IF EXISTS "sequel" CASCADE'
1589
- @d.create_schema(:sequel)
1590
- @d.create_schema(:sequel, :if_not_exists=>true) if @d.server_version >= 90300
1591
- @d.create_table(:sequel__test){Integer :a}
1592
- @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
1593
- end
1594
-
1595
- it "#create_trigger and #drop_trigger should create and drop triggers" do
1596
- @d.create_language(:plpgsql) if @d.server_version < 90000
1597
- @d.create_function(:tf, 'BEGIN IF NEW.value IS NULL THEN RAISE EXCEPTION \'Blah\'; END IF; RETURN NEW; END;', :language=>:plpgsql, :returns=>:trigger)
1598
- @d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).must_equal 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW EXECUTE PROCEDURE tf()'
1599
- @d.create_table(:test){String :name; Integer :value}
1600
- @d.create_trigger(:test, :identity, :tf, :each_row=>true)
1601
- @d[:test].insert(:name=>'a', :value=>1)
1602
- @d[:test].filter(:name=>'a').all.must_equal [{:name=>'a', :value=>1}]
1603
- proc{@d[:test].filter(:name=>'a').update(:value=>nil)}.must_raise(Sequel::DatabaseError)
1604
- @d[:test].filter(:name=>'a').all.must_equal [{:name=>'a', :value=>1}]
1605
- @d[:test].filter(:name=>'a').update(:value=>3)
1606
- @d[:test].filter(:name=>'a').all.must_equal [{:name=>'a', :value=>3}]
1607
- @d.send(:drop_trigger_sql, :test, :identity).must_equal 'DROP TRIGGER identity ON "test"'
1608
- @d.drop_trigger(:test, :identity)
1609
- @d.send(:create_trigger_sql, :test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).must_equal 'CREATE TRIGGER identity AFTER INSERT ON "test" EXECUTE PROCEDURE tf(1, \'a\')'
1610
- @d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).must_equal 'DROP TRIGGER IF EXISTS identity ON "test" CASCADE'
1611
- # Make sure if exists works
1612
- @d.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
1613
-
1614
- if @d.supports_trigger_conditions?
1615
- @d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true, :when=> {:new__name => 'b'}).must_equal %q{CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW WHEN ("new"."name" = 'b') EXECUTE PROCEDURE tf()}
1616
- @d.create_trigger(:test, :identity, :tf, :each_row=>true, :events => :update, :when=> {:new__name => 'b'})
1617
- @d[:test].filter(:name=>'a').update(:value=>nil)
1618
- @d[:test].filter(:name=>'a').all.must_equal [{:name=>'a', :value=>nil}]
1619
- proc{@d[:test].filter(:name=>'a').update(:name=>'b')}.must_raise(Sequel::DatabaseError)
1620
- @d[:test].filter(:name=>'a').all.must_equal [{:name=>'a', :value=>nil}]
1621
- @d.drop_trigger(:test, :identity)
1622
- end
1623
- end
1624
- end
1625
-
1626
- if DB.adapter_scheme == :postgres
1627
- describe "Postgres::Dataset #use_cursor" do
1628
- before(:all) do
1629
- @db = DB
1630
- @db.create_table!(:test_cursor){Integer :x}
1631
- @db.sqls.clear
1632
- @ds = @db[:test_cursor]
1633
- @db.transaction{1001.times{|i| @ds.insert(i)}}
1634
- end
1635
- after(:all) do
1636
- @db.drop_table?(:test_cursor)
1637
- end
1638
-
1639
- it "should return the same results as the non-cursor use" do
1640
- @ds.all.must_equal @ds.use_cursor.all
1641
- end
1642
-
1643
- it "should not swallow errors if closing cursor raises an error" do
1644
- proc do
1645
- @db.synchronize do |c|
1646
- @ds.use_cursor.each do |r|
1647
- @db.run "CLOSE sequel_cursor"
1648
- raise ArgumentError
1649
- end
1650
- end
1651
- end.must_raise(ArgumentError)
1652
- end
1653
-
1654
- it "should respect the :rows_per_fetch option" do
1655
- @db.sqls.clear
1656
- @ds.use_cursor.all
1657
- check_sqls do
1658
- @db.sqls.length.must_equal 6
1659
- @db.sqls.clear
1660
- end
1661
- @ds.use_cursor(:rows_per_fetch=>100).all
1662
- check_sqls do
1663
- @db.sqls.length.must_equal 15
1664
- end
1665
- end
1666
-
1667
- it "should respect the :hold=>true option for creating the cursor WITH HOLD and not using a transaction" do
1668
- @ds.use_cursor.each{@db.in_transaction?.must_equal true}
1669
- check_sqls{@db.sqls.any?{|s| s =~ /WITH HOLD/}.must_equal false}
1670
- @ds.use_cursor(:hold=>true).each{@db.in_transaction?.must_equal false}
1671
- check_sqls{@db.sqls.any?{|s| s =~ /WITH HOLD/}.must_equal true}
1672
- end
1673
-
1674
- it "should support updating individual rows based on a cursor" do
1675
- @db.transaction(:rollback=>:always) do
1676
- @ds.use_cursor(:rows_per_fetch=>1).each do |row|
1677
- @ds.where_current_of.update(:x=>Sequel.*(row[:x], 10))
1678
- end
1679
- @ds.select_order_map(:x).must_equal((0..1000).map{|x| x * 10})
1680
- end
1681
- @ds.select_order_map(:x).must_equal((0..1000).to_a)
1682
- end
1683
-
1684
- it "should respect the :cursor_name option" do
1685
- one_rows = []
1686
- two_rows = []
1687
- @ds.order(:x).use_cursor(:cursor_name => 'cursor_one').each do |one|
1688
- one_rows << one
1689
- if one[:x] % 1000 == 500
1690
- two_rows = []
1691
- @ds.order(:x).use_cursor(:cursor_name => 'cursor_two').each do |two|
1692
- two_rows << two
1693
- end
1694
- end
1695
- end
1696
- one_rows.must_equal two_rows
1697
- end
1698
-
1699
- it "should handle returning inside block" do
1700
- def @ds.check_return
1701
- use_cursor.each{|r| return}
1702
- end
1703
- @ds.check_return
1704
- @ds.all.must_equal @ds.use_cursor.all
1705
- end
1706
- end
1707
-
1708
- describe "Postgres::PG_NAMED_TYPES" do
1709
- before(:all) do
1710
- @db = DB
1711
- @old_cp = @db.conversion_procs[1013]
1712
- @db.conversion_procs.delete(1013)
1713
- Sequel::Postgres::PG_NAMED_TYPES[:oidvector] = lambda{|v| v.reverse}
1714
- @db.reset_conversion_procs
1715
- @db.register_array_type('oidvector')
1716
- end
1717
- after(:all) do
1718
- Sequel::Postgres::PG_NAMED_TYPES.delete(:oidvector)
1719
- @db.conversion_procs.delete(30)
1720
- @db.conversion_procs[1013] = @old_cp
1721
- @db.drop_table?(:foo)
1722
- @db.drop_enum(:foo_enum)
1723
- end
1724
-
1725
- it "should look up conversion procs by name" do
1726
- @db.create_table!(:foo){oidvector :bar}
1727
- @db[:foo].insert(Sequel.cast('21', :oidvector))
1728
- @db[:foo].get(:bar).must_equal '12'
1729
- end
1730
-
1731
- it "should handle array types of named types" do
1732
- @db.create_table!(:foo){column :bar, 'oidvector[]'}
1733
- @db[:foo].insert(Sequel.pg_array(['21'], :oidvector))
1734
- @db[:foo].get(:bar).must_equal ['12']
1735
- end
1736
-
1737
- it "should work with conversion procs on enums" do
1738
- @db.drop_enum(:foo_enum) rescue nil
1739
- @db.create_enum(:foo_enum, %w(foo bar))
1740
- @db.add_named_conversion_proc(:foo_enum){|string| string.reverse}
1741
- @db.create_table!(:foo){foo_enum :bar}
1742
- @db[:foo].insert(:bar => 'foo')
1743
- @db[:foo].get(:bar).must_equal 'foo'.reverse
1744
- end
1745
- end
1746
- end
1747
-
1748
- if ((DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc) && DB.server_version >= 90000
1749
- describe "Postgres::Database#copy_into" do
1750
- before(:all) do
1751
- @db = DB
1752
- @db.create_table!(:test_copy){Integer :x; Integer :y}
1753
- @ds = @db[:test_copy].order(:x, :y)
1754
- end
1755
- before do
1756
- @db[:test_copy].delete
1757
- end
1758
- after(:all) do
1759
- @db.drop_table?(:test_copy)
1760
- end
1761
-
1762
- it "should work with a :data option containing data in PostgreSQL text format" do
1763
- @db.copy_into(:test_copy, :data=>"1\t2\n3\t4\n")
1764
- @ds.select_map([:x, :y]).must_equal [[1, 2], [3, 4]]
1765
- end
1766
-
1767
- it "should work with :format=>:csv option and :data option containing data in CSV format" do
1768
- @db.copy_into(:test_copy, :format=>:csv, :data=>"1,2\n3,4\n")
1769
- @ds.select_map([:x, :y]).must_equal [[1, 2], [3, 4]]
1770
- end
1771
-
1772
- it "should respect given :options" do
1773
- @db.copy_into(:test_copy, :options=>"FORMAT csv, HEADER TRUE", :data=>"x,y\n1,2\n3,4\n")
1774
- @ds.select_map([:x, :y]).must_equal [[1, 2], [3, 4]]
1775
- end
1776
-
1777
- it "should respect given :options options when :format is used" do
1778
- @db.copy_into(:test_copy, :options=>"QUOTE '''', DELIMITER '|'", :format=>:csv, :data=>"'1'|'2'\n'3'|'4'\n")
1779
- @ds.select_map([:x, :y]).must_equal [[1, 2], [3, 4]]
1780
- end
1781
-
1782
- it "should accept :columns option to online copy the given columns" do
1783
- @db.copy_into(:test_copy, :data=>"1\t2\n3\t4\n", :columns=>[:y, :x])
1784
- @ds.select_map([:x, :y]).must_equal [[2, 1], [4, 3]]
1785
- end
1786
-
1787
- it "should accept a block and use returned values for the copy in data stream" do
1788
- buf = ["1\t2\n", "3\t4\n"]
1789
- @db.copy_into(:test_copy){buf.shift}
1790
- @ds.select_map([:x, :y]).must_equal [[1, 2], [3, 4]]
1791
- end
1792
-
1793
- it "should work correctly with a block and :format=>:csv" do
1794
- buf = ["1,2\n", "3,4\n"]
1795
- @db.copy_into(:test_copy, :format=>:csv){buf.shift}
1796
- @ds.select_map([:x, :y]).must_equal [[1, 2], [3, 4]]
1797
- end
1798
-
1799
- it "should accept an enumerable as the :data option" do
1800
- @db.copy_into(:test_copy, :data=>["1\t2\n", "3\t4\n"])
1801
- @ds.select_map([:x, :y]).must_equal [[1, 2], [3, 4]]
1802
- end
1803
-
1804
- it "should have an exception, cause a rollback of copied data and still have a usable connection" do
1805
- 2.times do
1806
- sent = false
1807
- proc{@db.copy_into(:test_copy){raise ArgumentError if sent; sent = true; "1\t2\n"}}.must_raise(ArgumentError)
1808
- @ds.select_map([:x, :y]).must_equal []
1809
- end
1810
- end
1811
-
1812
- it "should handle database errors with a rollback of copied data and still have a usable connection" do
1813
- 2.times do
1814
- proc{@db.copy_into(:test_copy, :data=>["1\t2\n", "3\ta\n"])}.must_raise(Sequel::DatabaseError)
1815
- @ds.select_map([:x, :y]).must_equal []
1816
- end
1817
- end
1818
-
1819
- it "should raise an Error if both :data and a block are provided" do
1820
- proc{@db.copy_into(:test_copy, :data=>["1\t2\n", "3\t4\n"]){}}.must_raise(Sequel::Error)
1821
- end
1822
-
1823
- it "should raise an Error if neither :data or a block are provided" do
1824
- proc{@db.copy_into(:test_copy)}.must_raise(Sequel::Error)
1825
- end
1826
- end
1827
-
1828
- describe "Postgres::Database#copy_table" do
1829
- before(:all) do
1830
- @db = DB
1831
- @db.create_table!(:test_copy){Integer :x; Integer :y}
1832
- ds = @db[:test_copy]
1833
- ds.insert(1, 2)
1834
- ds.insert(3, 4)
1835
- end
1836
- after(:all) do
1837
- @db.drop_table?(:test_copy)
1838
- end
1839
-
1840
- it "without a block or options should return a text version of the table as a single string" do
1841
- @db.copy_table(:test_copy).must_equal "1\t2\n3\t4\n"
1842
- end
1843
-
1844
- it "without a block and with :format=>:csv should return a csv version of the table as a single string" do
1845
- @db.copy_table(:test_copy, :format=>:csv).must_equal "1,2\n3,4\n"
1846
- end
1847
-
1848
- it "should treat string as SQL code" do
1849
- @db.copy_table('COPY "test_copy" TO STDOUT').must_equal "1\t2\n3\t4\n"
1850
- end
1851
-
1852
- it "should respect given :options options" do
1853
- @db.copy_table(:test_copy, :options=>"FORMAT csv, HEADER TRUE").must_equal "x,y\n1,2\n3,4\n"
1854
- end
1855
-
1856
- it "should respect given :options options when :format is used" do
1857
- @db.copy_table(:test_copy, :format=>:csv, :options=>"QUOTE '''', FORCE_QUOTE *").must_equal "'1','2'\n'3','4'\n"
1858
- end
1859
-
1860
- it "should accept dataset as first argument" do
1861
- @db.copy_table(@db[:test_copy].cross_join(:test_copy___tc).order(:test_copy__x, :test_copy__y, :tc__x, :tc__y)).must_equal "1\t2\t1\t2\n1\t2\t3\t4\n3\t4\t1\t2\n3\t4\t3\t4\n"
1862
- end
1863
-
1864
- it "with a block and no options should yield each row as a string in text format" do
1865
- buf = []
1866
- @db.copy_table(:test_copy){|b| buf << b}
1867
- buf.must_equal ["1\t2\n", "3\t4\n"]
1868
- end
1869
-
1870
- it "with a block and :format=>:csv should yield each row as a string in csv format" do
1871
- buf = []
1872
- @db.copy_table(:test_copy, :format=>:csv){|b| buf << b}
1873
- buf.must_equal ["1,2\n", "3,4\n"]
1874
- end
1875
-
1876
- it "should work fine when using a block that is terminated early with a following copy_table" do
1877
- buf = []
1878
- proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.must_raise(Sequel::DatabaseDisconnectError)
1879
- buf.must_equal ["1,2\n"]
1880
- buf.clear
1881
- proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.must_raise(Sequel::DatabaseDisconnectError)
1882
- buf.must_equal ["1,2\n"]
1883
- buf.clear
1884
- @db.copy_table(:test_copy){|b| buf << b}
1885
- buf.must_equal ["1\t2\n", "3\t4\n"]
1886
- end
1887
-
1888
- it "should work fine when using a block that is terminated early with a following regular query" do
1889
- buf = []
1890
- proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.must_raise(Sequel::DatabaseDisconnectError)
1891
- buf.must_equal ["1,2\n"]
1892
- buf.clear
1893
- proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.must_raise(Sequel::DatabaseDisconnectError)
1894
- buf.must_equal ["1,2\n"]
1895
- @db[:test_copy].select_order_map(:x).must_equal [1, 3]
1896
- end
1897
- end
1898
- end
1899
-
1900
- if DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && DB.server_version >= 90000
1901
- describe "Postgres::Database LISTEN/NOTIFY" do
1902
- before(:all) do
1903
- @db = DB
1904
- end
1905
-
1906
- it "should support listen and notify" do
1907
- notify_pid = @db.synchronize{|conn| conn.backend_pid}
1908
-
1909
- called = false
1910
- @db.listen('foo', :after_listen=>proc{@db.notify('foo')}) do |ev, pid, payload|
1911
- ev.must_equal 'foo'
1912
- pid.must_equal notify_pid
1913
- ['', nil].must_include(payload)
1914
- called = true
1915
- end.must_equal 'foo'
1916
- called.must_equal true
1917
-
1918
- # Check weird identifier names
1919
- called = false
1920
- @db.listen('FOO bar', :after_listen=>proc{@db.notify('FOO bar')}) do |ev, pid, payload|
1921
- ev.must_equal 'FOO bar'
1922
- pid.must_equal notify_pid
1923
- ['', nil].must_include(payload)
1924
- called = true
1925
- end.must_equal 'FOO bar'
1926
- called.must_equal true
1927
-
1928
- # Check identifier symbols
1929
- called = false
1930
- @db.listen(:foo, :after_listen=>proc{@db.notify(:foo)}) do |ev, pid, payload|
1931
- ev.must_equal 'foo'
1932
- pid.must_equal notify_pid
1933
- ['', nil].must_include(payload)
1934
- called = true
1935
- end.must_equal 'foo'
1936
- called.must_equal true
1937
-
1938
- called = false
1939
- @db.listen('foo', :after_listen=>proc{@db.notify('foo', :payload=>'bar')}) do |ev, pid, payload|
1940
- ev.must_equal 'foo'
1941
- pid.must_equal notify_pid
1942
- payload.must_equal 'bar'
1943
- called = true
1944
- end.must_equal 'foo'
1945
- called.must_equal true
1946
-
1947
- @db.listen('foo', :after_listen=>proc{@db.notify('foo')}).must_equal 'foo'
1948
-
1949
- called = false
1950
- called2 = false
1951
- i = 0
1952
- @db.listen(['foo', 'bar'], :after_listen=>proc{@db.notify('foo', :payload=>'bar'); @db.notify('bar', :payload=>'foo')}, :loop=>proc{i+=1}) do |ev, pid, payload|
1953
- if !called
1954
- ev.must_equal 'foo'
1955
- pid.must_equal notify_pid
1956
- payload.must_equal 'bar'
1957
- called = true
1958
- else
1959
- ev.must_equal 'bar'
1960
- pid.must_equal notify_pid
1961
- payload.must_equal 'foo'
1962
- called2 = true
1963
- break
1964
- end
1965
- end.must_equal nil
1966
- called.must_equal true
1967
- called2.must_equal true
1968
- i.must_equal 1
1969
- end
1970
-
1971
- it "should accept a :timeout option in listen" do
1972
- @db.listen('foo2', :timeout=>0.001).must_equal nil
1973
- called = false
1974
- @db.listen('foo2', :timeout=>0.001){|ev, pid, payload| called = true}.must_equal nil
1975
- called.must_equal false
1976
- i = 0
1977
- @db.listen('foo2', :timeout=>0.001, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.must_equal nil
1978
- i.must_equal 4
1979
-
1980
- called = false
1981
- i = 0
1982
- @db.listen('foo2', :timeout=>proc{i+=1; 0.001}){|ev, pid, payload| called = true}.must_equal nil
1983
- called.must_equal false
1984
- i.must_equal 1
1985
-
1986
- i = 0
1987
- t = 0
1988
- @db.listen('foo2', :timeout=>proc{t+=1; 0.001}, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.must_equal nil
1989
- called.must_equal false
1990
- t.must_equal 4
1991
-
1992
- end unless RUBY_PLATFORM =~ /mingw/ # Ruby freezes on this spec on this platform/version
1993
- end
1994
- end
1995
-
1996
- describe 'PostgreSQL special float handling' do
1997
- before do
1998
- @db = DB
1999
- @db.create_table!(:test5){Float :value}
2000
- @db.sqls.clear
2001
- @ds = @db[:test5]
2002
- end
2003
- after do
2004
- @db.drop_table?(:test5)
2005
- end
2006
-
2007
- check_sqls do
2008
- it 'should quote NaN' do
2009
- nan = 0.0/0.0
2010
- @ds.insert_sql(:value => nan).must_equal %q{INSERT INTO "test5" ("value") VALUES ('NaN')}
2011
- end
2012
-
2013
- it 'should quote +Infinity' do
2014
- inf = 1.0/0.0
2015
- @ds.insert_sql(:value => inf).must_equal %q{INSERT INTO "test5" ("value") VALUES ('Infinity')}
2016
- end
2017
-
2018
- it 'should quote -Infinity' do
2019
- inf = -1.0/0.0
2020
- @ds.insert_sql(:value => inf).must_equal %q{INSERT INTO "test5" ("value") VALUES ('-Infinity')}
2021
- end
2022
- end
2023
-
2024
- if DB.adapter_scheme == :postgres
2025
- it 'inserts NaN' do
2026
- nan = 0.0/0.0
2027
- @ds.insert(:value=>nan)
2028
- @ds.all[0][:value].nan?.must_equal true
2029
- end
2030
-
2031
- it 'inserts +Infinity' do
2032
- inf = 1.0/0.0
2033
- @ds.insert(:value=>inf)
2034
- @ds.all[0][:value].infinite?.must_be :>, 0
2035
- end
2036
-
2037
- it 'inserts -Infinity' do
2038
- inf = -1.0/0.0
2039
- @ds.insert(:value=>inf)
2040
- @ds.all[0][:value].infinite?.must_be :<, 0
2041
- end
2042
- end
2043
- end
2044
-
2045
- describe 'PostgreSQL array handling' do
2046
- before(:all) do
2047
- @db = DB
2048
- @ds = @db[:items]
2049
- @native = DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
2050
- @tp = lambda{@db.schema(:items).map{|a| a.last[:type]}}
2051
- end
2052
- after do
2053
- @db.drop_table?(:items)
2054
- end
2055
-
2056
- it 'insert and retrieve integer and float arrays of various sizes' do
2057
- @db.create_table!(:items) do
2058
- column :i2, 'int2[]'
2059
- column :i4, 'int4[]'
2060
- column :i8, 'int8[]'
2061
- column :r, 'real[]'
2062
- column :dp, 'double precision[]'
2063
- end
2064
- @tp.call.must_equal [:smallint_array, :integer_array, :bigint_array, :real_array, :float_array]
2065
- @ds.insert(Sequel.pg_array([1], :int2), Sequel.pg_array([nil, 2], :int4), Sequel.pg_array([3, nil], :int8), Sequel.pg_array([4, nil, 4.5], :real), Sequel.pg_array([5, nil, 5.5], "double precision"))
2066
- @ds.count.must_equal 1
2067
- rs = @ds.all
2068
- if @native
2069
- rs.must_equal [{:i2=>[1], :i4=>[nil, 2], :i8=>[3, nil], :r=>[4.0, nil, 4.5], :dp=>[5.0, nil, 5.5]}]
2070
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2071
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2072
- @ds.delete
2073
- @ds.insert(rs.first)
2074
- @ds.all.must_equal rs
2075
- end
2076
-
2077
- @ds.delete
2078
- @ds.insert(Sequel.pg_array([[1], [2]], :int2), Sequel.pg_array([[nil, 2], [3, 4]], :int4), Sequel.pg_array([[3, nil], [nil, nil]], :int8), Sequel.pg_array([[4, nil], [nil, 4.5]], :real), Sequel.pg_array([[5, nil], [nil, 5.5]], "double precision"))
2079
-
2080
- rs = @ds.all
2081
- if @native
2082
- rs.must_equal [{:i2=>[[1], [2]], :i4=>[[nil, 2], [3, 4]], :i8=>[[3, nil], [nil, nil]], :r=>[[4, nil], [nil, 4.5]], :dp=>[[5, nil], [nil, 5.5]]}]
2083
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2084
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2085
- @ds.delete
2086
- @ds.insert(rs.first)
2087
- @ds.all.must_equal rs
2088
- end
2089
- end
2090
-
2091
- it 'insert and retrieve decimal arrays' do
2092
- @db.create_table!(:items) do
2093
- column :n, 'numeric[]'
2094
- end
2095
- @tp.call.must_equal [:decimal_array]
2096
- @ds.insert(Sequel.pg_array([BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')], :numeric))
2097
- @ds.count.must_equal 1
2098
- rs = @ds.all
2099
- if @native
2100
- rs.must_equal [{:n=>[BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')]}]
2101
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2102
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2103
- @ds.delete
2104
- @ds.insert(rs.first)
2105
- @ds.all.must_equal rs
2106
- end
2107
-
2108
- @ds.delete
2109
- @ds.insert(Sequel.pg_array([[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]], :numeric))
2110
- rs = @ds.all
2111
- if @native
2112
- rs.must_equal [{:n=>[[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]]}]
2113
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2114
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2115
- @ds.delete
2116
- @ds.insert(rs.first)
2117
- @ds.all.must_equal rs
2118
- end
2119
- end
2120
-
2121
- it 'insert and retrieve string arrays' do
2122
- @db.create_table!(:items) do
2123
- column :c, 'char(4)[]'
2124
- column :vc, 'varchar[]'
2125
- column :t, 'text[]'
2126
- end
2127
- @tp.call.must_equal [:character_array, :varchar_array, :string_array]
2128
- @ds.insert(Sequel.pg_array(['a', nil, 'NULL', 'b"\'c'], 'char(4)'), Sequel.pg_array(['a', nil, 'NULL', 'b"\'c', '', ''], :varchar), Sequel.pg_array(['a', nil, 'NULL', 'b"\'c'], :text))
2129
- @ds.count.must_equal 1
2130
- rs = @ds.all
2131
- if @native
2132
- rs.must_equal [{:c=>['a ', nil, 'NULL', 'b"\'c'], :vc=>['a', nil, 'NULL', 'b"\'c', '', ''], :t=>['a', nil, 'NULL', 'b"\'c']}]
2133
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2134
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2135
- @ds.delete
2136
- @ds.insert(rs.first)
2137
- @ds.all.must_equal rs
2138
- end
2139
-
2140
- @ds.delete
2141
- @ds.insert(Sequel.pg_array([[['a'], [nil]], [['NULL'], ['b"\'c']]], 'char(4)'), Sequel.pg_array([[['a[],\\[\\]\\,\\""NULL",'], ['']], [['NULL'], ['b"\'c']]], :varchar), Sequel.pg_array([[['a'], [nil]], [['NULL'], ['b"\'c']]], :text))
2142
- rs = @ds.all
2143
- if @native
2144
- rs.must_equal [{:c=>[[['a '], [nil]], [['NULL'], ['b"\'c']]], :vc=>[[['a[],\\[\\]\\,\\""NULL",'], ['']], [['NULL'], ['b"\'c']]], :t=>[[['a'], [nil]], [['NULL'], ['b"\'c']]]}]
2145
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2146
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2147
- @ds.delete
2148
- @ds.insert(rs.first)
2149
- @ds.all.must_equal rs
2150
- end
2151
- end
2152
-
2153
- it 'insert and retrieve arrays of other types' do
2154
- @db.create_table!(:items) do
2155
- column :b, 'bool[]'
2156
- column :d, 'date[]'
2157
- column :t, 'time[]'
2158
- column :ts, 'timestamp[]'
2159
- column :tstz, 'timestamptz[]'
2160
- end
2161
- @tp.call.must_equal [:boolean_array, :date_array, :time_array, :datetime_array, :datetime_timezone_array]
2162
-
2163
- d = Date.today
2164
- t = Sequel::SQLTime.create(10, 20, 30)
2165
- ts = Time.local(2011, 1, 2, 3, 4, 5)
2166
-
2167
- @ds.insert(Sequel.pg_array([true, false], :bool), Sequel.pg_array([d, nil], :date), Sequel.pg_array([t, nil], :time), Sequel.pg_array([ts, nil], :timestamp), Sequel.pg_array([ts, nil], :timestamptz))
2168
- @ds.count.must_equal 1
2169
- rs = @ds.all
2170
- if @native
2171
- rs.must_equal [{:b=>[true, false], :d=>[d, nil], :t=>[t, nil], :ts=>[ts, nil], :tstz=>[ts, nil]}]
2172
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2173
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2174
- @ds.delete
2175
- @ds.insert(rs.first)
2176
- @ds.all.must_equal rs
2177
- end
2178
-
2179
- @db.create_table!(:items) do
2180
- column :ba, 'bytea[]'
2181
- column :tz, 'timetz[]'
2182
- column :o, 'oid[]'
2183
- end
2184
- @tp.call.must_equal [:blob_array, :time_timezone_array, :oid_array]
2185
- @ds.insert(Sequel.pg_array([Sequel.blob("a\0"), nil], :bytea), Sequel.pg_array([t, nil], :timetz), Sequel.pg_array([1, 2, 3], :oid))
2186
- @ds.count.must_equal 1
2187
- if @native
2188
- rs = @ds.all
2189
- rs.must_equal [{:ba=>[Sequel.blob("a\0"), nil], :tz=>[t, nil], :o=>[1, 2, 3]}]
2190
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2191
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2192
- @ds.delete
2193
- @ds.insert(rs.first)
2194
- @ds.all.must_equal rs
2195
- end
2196
-
2197
- @db.create_table!(:items) do
2198
- column :x, 'xml[]'
2199
- column :m, 'money[]'
2200
- column :b, 'bit[]'
2201
- column :vb, 'bit varying[]'
2202
- column :u, 'uuid[]'
2203
- column :xi, 'xid[]'
2204
- column :c, 'cid[]'
2205
- column :n, 'name[]'
2206
- column :o, 'oidvector[]'
2207
- end
2208
- @tp.call.must_equal [:xml_array, :money_array, :bit_array, :varbit_array, :uuid_array, :xid_array, :cid_array, :name_array, :oidvector_array]
2209
- @ds.insert(Sequel.pg_array(['<a></a>'], :xml),
2210
- Sequel.pg_array(['1'], :money),
2211
- Sequel.pg_array(['1'], :bit),
2212
- Sequel.pg_array(['10'], :varbit),
2213
- Sequel.pg_array(['c0f24910-39e7-11e4-916c-0800200c9a66'], :uuid),
2214
- Sequel.pg_array(['12'], :xid),
2215
- Sequel.pg_array(['12'], :cid),
2216
- Sequel.pg_array(['N'], :name),
2217
- Sequel.pg_array(['1 2'], :oidvector))
2218
- @ds.count.must_equal 1
2219
- if @native
2220
- rs = @ds.all
2221
- r = rs.first
2222
- m = r.delete(:m)
2223
- m.class.must_equal(Sequel::Postgres::PGArray)
2224
- m.to_a.must_be_kind_of(Array)
2225
- m.first.must_be_kind_of(String)
2226
- r.must_be(:==, :x=>['<a></a>'], :b=>['1'], :vb=>['10'], :u=>['c0f24910-39e7-11e4-916c-0800200c9a66'], :xi=>['12'], :c=>['12'], :n=>['N'], :o=>['1 2'])
2227
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2228
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2229
- r[:m] = m
2230
- @ds.delete
2231
- @ds.insert(r)
2232
- @ds.all.must_equal rs
2233
- end
2234
- end
2235
-
2236
- it 'insert and retrieve empty arrays' do
2237
- @db.create_table!(:items) do
2238
- column :n, 'integer[]'
2239
- end
2240
- @ds.insert(:n=>Sequel.pg_array([], :integer))
2241
- @ds.count.must_equal 1
2242
- if @native
2243
- rs = @ds.all
2244
- rs.must_equal [{:n=>[]}]
2245
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2246
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2247
- @ds.delete
2248
- @ds.insert(rs.first)
2249
- @ds.all.must_equal rs
2250
- end
2251
- end
2252
-
2253
- it 'convert ruby array :default values' do
2254
- @db.create_table!(:items) do
2255
- column :n, 'integer[]', :default=>[]
2256
- end
2257
- @ds.insert
2258
- @ds.count.must_equal 1
2259
- if @native
2260
- rs = @ds.all
2261
- rs.must_equal [{:n=>[]}]
2262
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2263
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2264
- @ds.delete
2265
- @ds.insert(rs.first)
2266
- @ds.all.must_equal rs
2267
- end
2268
- end
2269
-
2270
- it 'insert and retrieve custom array types' do
2271
- int2vector = Class.new do
2272
- attr_reader :array
2273
- def initialize(array)
2274
- @array = array
2275
- end
2276
- def sql_literal_append(ds, sql)
2277
- sql << "'#{array.join(' ')}'"
2278
- end
2279
- def ==(other)
2280
- if other.is_a?(self.class)
2281
- array == other.array
2282
- else
2283
- super
2284
- end
2285
- end
2286
- end
2287
- @db.register_array_type(:int2vector){|s| int2vector.new(s.split.map{|i| i.to_i})}
2288
- @db.create_table!(:items) do
2289
- column :b, 'int2vector[]'
2290
- end
2291
- @tp.call.must_equal [:int2vector_array]
2292
- int2v = int2vector.new([1, 2])
2293
- @ds.insert(Sequel.pg_array([int2v], :int2vector))
2294
- @ds.count.must_equal 1
2295
- rs = @ds.all
2296
- if @native
2297
- rs.must_equal [{:b=>[int2v]}]
2298
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2299
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2300
- @ds.delete
2301
- @ds.insert(rs.first)
2302
- @ds.all.must_equal rs
2303
- end
2304
- end
2305
-
2306
- it 'retrieve arrays with explicit bounds' do
2307
- @db.create_table!(:items) do
2308
- column :n, 'integer[]'
2309
- end
2310
- @ds.insert(:n=>"[0:1]={2,3}")
2311
- rs = @ds.all
2312
- rs.must_equal [{:n=>[2,3]}]
2313
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2314
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2315
- @ds.delete
2316
- @ds.insert(rs.first)
2317
- @ds.all.must_equal rs
2318
-
2319
- @ds.delete
2320
- @ds.insert(:n=>"[0:1][0:0]={{2},{3}}")
2321
- rs = @ds.all
2322
- rs.must_equal [{:n=>[[2], [3]]}]
2323
- rs.first.values.each{|v| v.class.must_equal(Sequel::Postgres::PGArray)}
2324
- rs.first.values.each{|v| v.to_a.must_be_kind_of(Array)}
2325
- @ds.delete
2326
- @ds.insert(rs.first)
2327
- @ds.all.must_equal rs
2328
- end if DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
2329
-
2330
- it 'use arrays in bound variables' do
2331
- @db.create_table!(:items) do
2332
- column :i, 'int4[]'
2333
- end
2334
- @ds.call(:insert, {:i=>[1,2]}, {:i=>:$i})
2335
- @ds.get(:i).must_equal [1, 2]
2336
- @ds.filter(:i=>:$i).call(:first, :i=>[1,2]).must_equal(:i=>[1,2])
2337
- @ds.filter(:i=>:$i).call(:first, :i=>[1,3]).must_equal nil
2338
-
2339
- # NULL values
2340
- @ds.delete
2341
- @ds.call(:insert, {:i=>[nil,nil]}, {:i=>:$i})
2342
- @ds.first.must_equal(:i=>[nil, nil])
2343
-
2344
- @db.create_table!(:items) do
2345
- column :i, 'text[]'
2346
- end
2347
- a = ["\"\\\\\"{}\n\t\r \v\b123afP", 'NULL', nil, '']
2348
- @ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a))
2349
- @ds.get(:i).must_equal a
2350
- @ds.filter(:i=>:$i).call(:first, :i=>a).must_equal(:i=>a)
2351
- @ds.filter(:i=>:$i).call(:first, :i=>['', nil, nil, 'a']).must_equal nil
2352
-
2353
- @db.create_table!(:items) do
2354
- column :i, 'date[]'
2355
- end
2356
- a = [Date.today]
2357
- @ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'date'))
2358
- @ds.get(:i).must_equal a
2359
- @ds.filter(:i=>:$i).call(:first, :i=>a).must_equal(:i=>a)
2360
- @ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([Date.today-1], 'date')).must_equal nil
2361
-
2362
- @db.create_table!(:items) do
2363
- column :i, 'timestamp[]'
2364
- end
2365
- a = [Time.local(2011, 1, 2, 3, 4, 5)]
2366
- @ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'timestamp'))
2367
- @ds.get(:i).must_equal a
2368
- @ds.filter(:i=>:$i).call(:first, :i=>a).must_equal(:i=>a)
2369
- @ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([a.first-1], 'timestamp')).must_equal nil
2370
-
2371
- @db.create_table!(:items) do
2372
- column :i, 'boolean[]'
2373
- end
2374
- a = [true, false]
2375
- @ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'boolean'))
2376
- @ds.get(:i).must_equal a
2377
- @ds.filter(:i=>:$i).call(:first, :i=>a).must_equal(:i=>a)
2378
- @ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([false, true], 'boolean')).must_equal nil
2379
-
2380
- @db.create_table!(:items) do
2381
- column :i, 'bytea[]'
2382
- end
2383
- a = [Sequel.blob("a\0'\"")]
2384
- @ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'bytea'))
2385
- @ds.get(:i).must_equal a
2386
- @ds.filter(:i=>:$i).call(:first, :i=>a).must_equal(:i=>a)
2387
- @ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([Sequel.blob("b\0")], 'bytea')).must_equal nil
2388
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
2389
-
2390
- it 'with models' do
2391
- @db.create_table!(:items) do
2392
- primary_key :id
2393
- column :i, 'integer[]'
2394
- column :f, 'double precision[]'
2395
- column :d, 'numeric[]'
2396
- column :t, 'text[]'
2397
- end
2398
- c = Class.new(Sequel::Model(@db[:items]))
2399
- c.plugin :pg_typecast_on_load, :i, :f, :d, :t unless @native
2400
- h = {:i=>[1,2, nil], :f=>[[1, 2.5], [3, 4.5]], :d=>[1, BigDecimal.new('1.000000000000000000001')], :t=>[%w'a b c', ['NULL', nil, '1']]}
2401
- o = c.create(h)
2402
- o.i.must_equal [1, 2, nil]
2403
- o.f.must_equal [[1, 2.5], [3, 4.5]]
2404
- o.d.must_equal [BigDecimal.new('1'), BigDecimal.new('1.000000000000000000001')]
2405
- o.t.must_equal [%w'a b c', ['NULL', nil, '1']]
2406
- c.where(:i=>o.i, :f=>o.f, :d=>o.d, :t=>o.t).all.must_equal [o]
2407
- o2 = c.new(h)
2408
- c.where(:i=>o2.i, :f=>o2.f, :d=>o2.d, :t=>o2.t).all.must_equal [o]
2409
-
2410
- @db.create_table!(:items) do
2411
- primary_key :id
2412
- column :i, 'int2[]'
2413
- column :f, 'real[]'
2414
- column :d, 'numeric(30,28)[]'
2415
- column :t, 'varchar[]'
2416
- end
2417
- c = Class.new(Sequel::Model(@db[:items]))
2418
- c.plugin :pg_typecast_on_load, :i, :f, :d, :t unless @native
2419
- o = c.create(:i=>[1,2, nil], :f=>[[1, 2.5], [3, 4.5]], :d=>[1, BigDecimal.new('1.000000000000000000001')], :t=>[%w'a b c', ['NULL', nil, '1']])
2420
- o.i.must_equal [1, 2, nil]
2421
- o.f.must_equal [[1, 2.5], [3, 4.5]]
2422
- o.d.must_equal [BigDecimal.new('1'), BigDecimal.new('1.000000000000000000001')]
2423
- o.t.must_equal [%w'a b c', ['NULL', nil, '1']]
2424
- c.where(:i=>o.i, :f=>o.f, :d=>o.d, :t=>o.t).all.must_equal [o]
2425
- o2 = c.new(h)
2426
- c.where(:i=>o2.i, :f=>o2.f, :d=>o2.d, :t=>o2.t).all.must_equal [o]
2427
- end
2428
-
2429
- it 'operations/functions with pg_array_ops' do
2430
- Sequel.extension :pg_array_ops
2431
- @db.create_table!(:items){column :i, 'integer[]'; column :i2, 'integer[]'; column :i3, 'integer[]'; column :i4, 'integer[]'; column :i5, 'integer[]'}
2432
- @ds.insert(Sequel.pg_array([1, 2, 3]), Sequel.pg_array([2, 1]), Sequel.pg_array([4, 4]), Sequel.pg_array([[5, 5], [4, 3]]), Sequel.pg_array([1, nil, 5]))
2433
-
2434
- @ds.get(Sequel.pg_array(:i) > :i3).must_equal false
2435
- @ds.get(Sequel.pg_array(:i3) > :i).must_equal true
2436
-
2437
- @ds.get(Sequel.pg_array(:i) >= :i3).must_equal false
2438
- @ds.get(Sequel.pg_array(:i) >= :i).must_equal true
2439
-
2440
- @ds.get(Sequel.pg_array(:i3) < :i).must_equal false
2441
- @ds.get(Sequel.pg_array(:i) < :i3).must_equal true
2442
-
2443
- @ds.get(Sequel.pg_array(:i3) <= :i).must_equal false
2444
- @ds.get(Sequel.pg_array(:i) <= :i).must_equal true
2445
-
2446
- @ds.get(Sequel.expr(5=>Sequel.pg_array(:i).any)).must_equal false
2447
- @ds.get(Sequel.expr(1=>Sequel.pg_array(:i).any)).must_equal true
2448
-
2449
- @ds.get(Sequel.expr(1=>Sequel.pg_array(:i3).all)).must_equal false
2450
- @ds.get(Sequel.expr(4=>Sequel.pg_array(:i3).all)).must_equal true
2451
-
2452
- @ds.get(Sequel.expr(1=>Sequel.pg_array(:i)[1..1].any)).must_equal true
2453
- @ds.get(Sequel.expr(2=>Sequel.pg_array(:i)[1..1].any)).must_equal false
2454
-
2455
- @ds.get(Sequel.pg_array(:i2)[1]).must_equal 2
2456
- @ds.get(Sequel.pg_array(:i2)[1]).must_equal 2
2457
- @ds.get(Sequel.pg_array(:i2)[2]).must_equal 1
2458
-
2459
- @ds.get(Sequel.pg_array(:i4)[2][1]).must_equal 4
2460
- @ds.get(Sequel.pg_array(:i4)[2][2]).must_equal 3
2461
-
2462
- @ds.get(Sequel.pg_array(:i).contains(:i2)).must_equal true
2463
- @ds.get(Sequel.pg_array(:i).contains(:i3)).must_equal false
2464
-
2465
- @ds.get(Sequel.pg_array(:i2).contained_by(:i)).must_equal true
2466
- @ds.get(Sequel.pg_array(:i).contained_by(:i2)).must_equal false
2467
-
2468
- @ds.get(Sequel.pg_array(:i).overlaps(:i2)).must_equal true
2469
- @ds.get(Sequel.pg_array(:i2).overlaps(:i3)).must_equal false
2470
-
2471
- @ds.get(Sequel.pg_array(:i).dims).must_equal '[1:3]'
2472
- @ds.get(Sequel.pg_array(:i).length).must_equal 3
2473
- @ds.get(Sequel.pg_array(:i).lower).must_equal 1
2474
-
2475
- if @db.server_version >= 80400
2476
- @ds.select(Sequel.pg_array(:i).unnest).from_self.count.must_equal 3
2477
- end
2478
- if @db.server_version >= 90000
2479
- @ds.get(Sequel.pg_array(:i5).join).must_equal '15'
2480
- @ds.get(Sequel.pg_array(:i5).join(':')).must_equal '1:5'
2481
- @ds.get(Sequel.pg_array(:i5).join(':', '*')).must_equal '1:*:5'
2482
- end
2483
- if @db.server_version >= 90300
2484
- @ds.get(Sequel.pg_array(:i5).remove(1).length).must_equal 2
2485
- @ds.get(Sequel.pg_array(:i5).replace(1, 4).contains([1])).must_equal false
2486
- @ds.get(Sequel.pg_array(:i5).replace(1, 4).contains([4])).must_equal true
2487
- end
2488
- if @db.server_version >= 90400
2489
- @ds.get(Sequel.pg_array(:i).cardinality).must_equal 3
2490
- @ds.get(Sequel.pg_array(:i4).cardinality).must_equal 4
2491
- @ds.get(Sequel.pg_array(:i5).cardinality).must_equal 3
2492
-
2493
- @ds.from{Sequel.pg_array([1,2,3]).op.unnest([4,5,6], [7,8]).as(:t1, [:a, :b, :c])}.select_order_map([:a, :b, :c]).must_equal [[1, 4, 7], [2, 5, 8], [3, 6, nil]]
2494
- end
2495
-
2496
- if @native
2497
- @ds.get(Sequel.pg_array(:i).push(4)).must_equal [1, 2, 3, 4]
2498
- @ds.get(Sequel.pg_array(:i).unshift(4)).must_equal [4, 1, 2, 3]
2499
- @ds.get(Sequel.pg_array(:i).concat(:i2)).must_equal [1, 2, 3, 2, 1]
2500
- end
2501
-
2502
- if @db.type_supported?(:hstore)
2503
- Sequel.extension :pg_hstore_ops
2504
- @db.get(Sequel.pg_array(['a', 'b']).op.hstore['a']).must_equal 'b'
2505
- @db.get(Sequel.pg_array(['a', 'b']).op.hstore(['c', 'd'])['a']).must_equal 'c'
2506
- end
2507
- end
2508
- end
2509
-
2510
- describe 'PostgreSQL hstore handling' do
2511
- before(:all) do
2512
- @db = DB
2513
- @ds = @db[:items]
2514
- @h = {'a'=>'b', 'c'=>nil, 'd'=>'NULL', 'e'=>'\\\\" \\\' ,=>'}
2515
- @native = DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
2516
- end
2517
- after do
2518
- @db.drop_table?(:items)
2519
- end
2520
-
2521
- it 'insert and retrieve hstore values' do
2522
- @db.create_table!(:items) do
2523
- column :h, :hstore
2524
- end
2525
- @ds.insert(Sequel.hstore(@h))
2526
- @ds.count.must_equal 1
2527
- if @native
2528
- rs = @ds.all
2529
- v = rs.first[:h]
2530
- v.must_equal @h
2531
- v.class.must_equal(Sequel::Postgres::HStore)
2532
- v.to_hash.must_be_kind_of(Hash)
2533
- v.to_hash.must_equal @h
2534
- @ds.delete
2535
- @ds.insert(rs.first)
2536
- @ds.all.must_equal rs
2537
- end
2538
- end
2539
-
2540
- it 'insert and retrieve hstore[] values' do
2541
- @db.create_table!(:items) do
2542
- column :h, 'hstore[]'
2543
- end
2544
- @ds.insert(Sequel.pg_array([Sequel.hstore(@h)], :hstore))
2545
- @ds.count.must_equal 1
2546
- if @native
2547
- rs = @ds.all
2548
- v = rs.first[:h].first
2549
- v.class.must_equal(Sequel::Postgres::HStore)
2550
- v.to_hash.must_be_kind_of(Hash)
2551
- v.to_hash.must_equal @h
2552
- @ds.delete
2553
- @ds.insert(rs.first)
2554
- @ds.all.must_equal rs
2555
- end
2556
- end
2557
-
2558
- it 'use hstore in bound variables' do
2559
- @db.create_table!(:items) do
2560
- column :i, :hstore
2561
- end
2562
- @ds.call(:insert, {:i=>Sequel.hstore(@h)}, {:i=>:$i})
2563
- @ds.get(:i).must_equal @h
2564
- @ds.filter(:i=>:$i).call(:first, :i=>Sequel.hstore(@h)).must_equal(:i=>@h)
2565
- @ds.filter(:i=>:$i).call(:first, :i=>Sequel.hstore({})).must_equal nil
2566
-
2567
- @ds.delete
2568
- @ds.call(:insert, {:i=>Sequel.hstore('a'=>nil)}, {:i=>:$i})
2569
- @ds.get(:i).must_equal Sequel.hstore('a'=>nil)
2570
-
2571
- @ds.delete
2572
- @ds.call(:insert, {:i=>@h}, {:i=>:$i})
2573
- @ds.get(:i).must_equal @h
2574
- @ds.filter(:i=>:$i).call(:first, :i=>@h).must_equal(:i=>@h)
2575
- @ds.filter(:i=>:$i).call(:first, :i=>{}).must_equal nil
2576
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
2577
-
2578
- it 'with models and associations' do
2579
- @db.create_table!(:items) do
2580
- primary_key :id
2581
- column :h, :hstore
2582
- end
2583
- c = Class.new(Sequel::Model(@db[:items])) do
2584
- def self.name
2585
- 'Item'
2586
- end
2587
- unrestrict_primary_key
2588
- def item_id
2589
- h['item_id'].to_i if h
2590
- end
2591
- def left_item_id
2592
- h['left_item_id'].to_i if h
2593
- end
2594
- end
2595
- Sequel.extension :pg_hstore_ops
2596
- c.plugin :many_through_many
2597
- c.plugin :pg_typecast_on_load, :h unless @native
2598
-
2599
- h = {'item_id'=>"2", 'left_item_id'=>"1"}
2600
- o2 = c.create(:id=>2)
2601
- o = c.create(:id=>1, :h=>h)
2602
- o.h.must_equal h
2603
-
2604
- c.many_to_one :item, :class=>c, :key_column=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer)
2605
- c.one_to_many :items, :class=>c, :key=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer), :key_method=>:item_id
2606
- c.many_to_many :related_items, :class=>c, :join_table=>:items___i, :left_key=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer), :right_key=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer)
2607
-
2608
- c.many_to_one :other_item, :class=>c, :key=>:id, :primary_key_method=>:item_id, :primary_key=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer), :reciprocal=>:other_items
2609
- c.one_to_many :other_items, :class=>c, :primary_key=>:item_id, :key=>:id, :primary_key_column=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer), :reciprocal=>:other_item
2610
- c.many_to_many :other_related_items, :class=>c, :join_table=>:items___i, :left_key=>:id, :right_key=>:id,
2611
- :left_primary_key_column=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer),
2612
- :left_primary_key=>:left_item_id,
2613
- :right_primary_key=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer),
2614
- :right_primary_key_method=>:left_item_id
2615
-
2616
- c.many_through_many :mtm_items, [
2617
- [:items, Sequel.cast(Sequel.hstore(:h)['item_id'], Integer), Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer)],
2618
- [:items, Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer), Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer)]
2619
- ],
2620
- :class=>c,
2621
- :left_primary_key_column=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer),
2622
- :left_primary_key=>:item_id,
2623
- :right_primary_key=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer),
2624
- :right_primary_key_method=>:left_item_id
2625
-
2626
- # Lazily Loading
2627
- o.item.must_equal o2
2628
- o2.items.must_equal [o]
2629
- o.related_items.must_equal [o2]
2630
- o2.other_item.must_equal o
2631
- o.other_items.must_equal [o2]
2632
- o.other_related_items.must_equal [o]
2633
- o.mtm_items.must_equal [o]
2634
-
2635
- # Eager Loading via eager
2636
- os = c.eager(:item, :related_items, :other_items, :other_related_items, :mtm_items).where(:id=>1).all.first
2637
- os.item.must_equal o2
2638
- os.related_items.must_equal [o2]
2639
- os.other_items.must_equal [o2]
2640
- os.other_related_items.must_equal [o]
2641
- os.mtm_items.must_equal [o]
2642
- os = c.eager(:items, :other_item).where(:id=>2).all.first
2643
- os.items.must_equal [o]
2644
- os.other_item.must_equal o
2645
-
2646
- # Eager Loading via eager_graph
2647
- c.eager_graph(:item).where(:items__id=>1).all.first.item.must_equal o2
2648
- c.eager_graph(:items).where(:items__id=>2).all.first.items.must_equal [o]
2649
- c.eager_graph(:related_items).where(:items__id=>1).all.first.related_items.must_equal [o2]
2650
- c.eager_graph(:other_item).where(:items__id=>2).all.first.other_item.must_equal o
2651
- c.eager_graph(:other_items).where(:items__id=>1).all.first.other_items.must_equal [o2]
2652
- c.eager_graph(:other_related_items).where(:items__id=>1).all.first.other_related_items.must_equal [o]
2653
- c.eager_graph(:mtm_items).where(:items__id=>1).all.first.mtm_items.must_equal [o]
2654
-
2655
- # Filter By Associations - Model Instances
2656
- c.filter(:item=>o2).all.must_equal [o]
2657
- c.filter(:items=>o).all.must_equal [o2]
2658
- c.filter(:related_items=>o2).all.must_equal [o]
2659
- c.filter(:other_item=>o).all.must_equal [o2]
2660
- c.filter(:other_items=>o2).all.must_equal [o]
2661
- c.filter(:other_related_items=>o).all.must_equal [o]
2662
- c.filter(:mtm_items=>o).all.must_equal [o]
2663
-
2664
- # Filter By Associations - Model Datasets
2665
- c.filter(:item=>c.filter(:id=>o2.id)).all.must_equal [o]
2666
- c.filter(:items=>c.filter(:id=>o.id)).all.must_equal [o2]
2667
- c.filter(:related_items=>c.filter(:id=>o2.id)).all.must_equal [o]
2668
- c.filter(:other_item=>c.filter(:id=>o.id)).all.must_equal [o2]
2669
- c.filter(:other_items=>c.filter(:id=>o2.id)).all.must_equal [o]
2670
- c.filter(:other_related_items=>c.filter(:id=>o.id)).all.must_equal [o]
2671
- c.filter(:mtm_items=>c.filter(:id=>o.id)).all.must_equal [o]
2672
- end
2673
-
2674
- it 'operations/functions with pg_hstore_ops' do
2675
- Sequel.extension :pg_hstore_ops, :pg_array_ops
2676
- @db.create_table!(:items){hstore :h1; hstore :h2; hstore :h3; String :t}
2677
- @ds.insert(Sequel.hstore('a'=>'b', 'c'=>nil), Sequel.hstore('a'=>'b'), Sequel.hstore('d'=>'e'))
2678
- h1 = Sequel.hstore(:h1)
2679
- h2 = Sequel.hstore(:h2)
2680
- h3 = Sequel.hstore(:h3)
2681
-
2682
- @ds.get(h1['a']).must_equal 'b'
2683
- @ds.get(h1['d']).must_equal nil
2684
-
2685
- @ds.get(h2.concat(h3).keys.length).must_equal 2
2686
- @ds.get(h1.concat(h3).keys.length).must_equal 3
2687
- @ds.get(h2.merge(h3).keys.length).must_equal 2
2688
- @ds.get(h1.merge(h3).keys.length).must_equal 3
2689
-
2690
- @ds.get(h1.contain_all(%w'a c')).must_equal true
2691
- @ds.get(h1.contain_all(%w'a d')).must_equal false
2692
-
2693
- @ds.get(h1.contain_any(%w'a d')).must_equal true
2694
- @ds.get(h1.contain_any(%w'e d')).must_equal false
2695
-
2696
- @ds.get(h1.contains(h2)).must_equal true
2697
- @ds.get(h1.contains(h3)).must_equal false
2698
-
2699
- @ds.get(h2.contained_by(h1)).must_equal true
2700
- @ds.get(h2.contained_by(h3)).must_equal false
2701
-
2702
- @ds.get(h1.defined('a')).must_equal true
2703
- @ds.get(h1.defined('c')).must_equal false
2704
- @ds.get(h1.defined('d')).must_equal false
2705
-
2706
- @ds.get(h1.delete('a')['c']).must_equal nil
2707
- @ds.get(h1.delete(%w'a d')['c']).must_equal nil
2708
- @ds.get(h1.delete(h2)['c']).must_equal nil
2709
-
2710
- @ds.from(Sequel.hstore('a'=>'b', 'c'=>nil).op.each).order(:key).all.must_equal [{:key=>'a', :value=>'b'}, {:key=>'c', :value=>nil}]
2711
-
2712
- @ds.get(h1.has_key?('c')).must_equal true
2713
- @ds.get(h1.include?('c')).must_equal true
2714
- @ds.get(h1.key?('c')).must_equal true
2715
- @ds.get(h1.member?('c')).must_equal true
2716
- @ds.get(h1.exist?('c')).must_equal true
2717
- @ds.get(h1.has_key?('d')).must_equal false
2718
- @ds.get(h1.include?('d')).must_equal false
2719
- @ds.get(h1.key?('d')).must_equal false
2720
- @ds.get(h1.member?('d')).must_equal false
2721
- @ds.get(h1.exist?('d')).must_equal false
2722
-
2723
- @ds.get(h1.hstore.hstore.hstore.keys.length).must_equal 2
2724
- @ds.get(h1.keys.length).must_equal 2
2725
- @ds.get(h2.keys.length).must_equal 1
2726
- @ds.get(h1.akeys.length).must_equal 2
2727
- @ds.get(h2.akeys.length).must_equal 1
2728
-
2729
- @ds.from(Sequel.hstore('t'=>'s').op.populate(Sequel::SQL::Cast.new(nil, :items))).select_map(:t).must_equal ['s']
2730
- @ds.from(:items___i).select(Sequel.hstore('t'=>'s').op.record_set(:i).as(:r)).from_self(:alias=>:s).select(Sequel.lit('(r).*')).from_self.select_map(:t).must_equal ['s']
2731
-
2732
- @ds.from(Sequel.hstore('t'=>'s', 'a'=>'b').op.skeys.as(:s)).select_order_map(:s).must_equal %w'a t'
2733
- @ds.from((Sequel.hstore('t'=>'s', 'a'=>'b').op - 'a').skeys.as(:s)).select_order_map(:s).must_equal %w't'
2734
-
2735
- @ds.get(h1.slice(%w'a c').keys.length).must_equal 2
2736
- @ds.get(h1.slice(%w'd c').keys.length).must_equal 1
2737
- @ds.get(h1.slice(%w'd e').keys.length).must_equal nil
2738
-
2739
- @ds.from(Sequel.hstore('t'=>'s', 'a'=>'b').op.svals.as(:s)).select_order_map(:s).must_equal %w'b s'
2740
-
2741
- @ds.get(h1.to_array.length).must_equal 4
2742
- @ds.get(h2.to_array.length).must_equal 2
2743
-
2744
- @ds.get(h1.to_matrix.length).must_equal 2
2745
- @ds.get(h2.to_matrix.length).must_equal 1
2746
-
2747
- @ds.get(h1.values.length).must_equal 2
2748
- @ds.get(h2.values.length).must_equal 1
2749
- @ds.get(h1.avals.length).must_equal 2
2750
- @ds.get(h2.avals.length).must_equal 1
2751
- end
2752
- end if DB.type_supported?(:hstore)
2753
-
2754
- describe 'PostgreSQL json type' do
2755
- before(:all) do
2756
- @db = DB
2757
- @ds = @db[:items]
2758
- @a = [1, 2, {'a'=>'b'}, 3.0]
2759
- @h = {'a'=>'b', '1'=>[3, 4, 5]}
2760
- @native = DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
2761
- end
2762
- after do
2763
- @db.drop_table?(:items)
2764
- end
2765
-
2766
- json_types = [:json]
2767
- json_types << :jsonb if DB.server_version >= 90400
2768
- json_types.each do |json_type|
2769
- json_array_type = "#{json_type}[]"
2770
- pg_json = lambda{|v| Sequel.send(:"pg_#{json_type}", v)}
2771
-
2772
- it 'insert and retrieve json values' do
2773
- hash_class = json_type == :jsonb ? Sequel::Postgres::JSONBHash : Sequel::Postgres::JSONHash
2774
- array_class = json_type == :jsonb ? Sequel::Postgres::JSONBArray : Sequel::Postgres::JSONArray
2775
-
2776
- @db.create_table!(:items){column :j, json_type}
2777
- @ds.insert(pg_json.call(@h))
2778
- @ds.count.must_equal 1
2779
- if @native
2780
- rs = @ds.all
2781
- v = rs.first[:j]
2782
- v.class.must_equal(hash_class)
2783
- v.to_hash.must_be_kind_of(Hash)
2784
- v.must_equal @h
2785
- v.to_hash.must_equal @h
2786
- @ds.delete
2787
- @ds.insert(rs.first)
2788
- @ds.all.must_equal rs
2789
- end
2790
-
2791
- @ds.delete
2792
- @ds.insert(pg_json.call(@a))
2793
- @ds.count.must_equal 1
2794
- if @native
2795
- rs = @ds.all
2796
- v = rs.first[:j]
2797
- v.class.must_equal(array_class)
2798
- v.to_a.must_be_kind_of(Array)
2799
- v.must_equal @a
2800
- v.to_a.must_equal @a
2801
- @ds.delete
2802
- @ds.insert(rs.first)
2803
- @ds.all.must_equal rs
2804
- end
2805
- end
2806
-
2807
- it 'insert and retrieve json[] values' do
2808
- @db.create_table!(:items){column :j, json_array_type}
2809
- j = Sequel.pg_array([pg_json.call('a'=>1), pg_json.call(['b', 2])])
2810
- @ds.insert(j)
2811
- @ds.count.must_equal 1
2812
- if @native
2813
- rs = @ds.all
2814
- v = rs.first[:j]
2815
- v.class.must_equal(Sequel::Postgres::PGArray)
2816
- v.to_a.must_be_kind_of(Array)
2817
- v.must_equal j
2818
- v.to_a.must_equal j
2819
- @ds.delete
2820
- @ds.insert(rs.first)
2821
- @ds.all.must_equal rs
2822
- end
2823
- end
2824
-
2825
- it 'with models' do
2826
- @db.create_table!(:items) do
2827
- primary_key :id
2828
- column :h, json_type
2829
- end
2830
- c = Class.new(Sequel::Model(@db[:items]))
2831
- c.plugin :pg_typecast_on_load, :h unless @native
2832
- c.create(:h=>pg_json.call(@h)).h.must_equal @h
2833
- c.create(:h=>pg_json.call(@a)).h.must_equal @a
2834
- end
2835
-
2836
- it 'use json in bound variables' do
2837
- @db.create_table!(:items){column :i, json_type}
2838
- @ds.call(:insert, {:i=>pg_json.call(@h)}, {:i=>:$i})
2839
- @ds.get(:i).must_equal @h
2840
-
2841
- @ds.delete
2842
- @ds.call(:insert, {:i=>pg_json.call('a'=>nil)}, {:i=>:$i})
2843
- @ds.get(:i).must_equal pg_json.call('a'=>nil)
2844
-
2845
- @db.create_table!(:items){column :i, json_array_type}
2846
- j = Sequel.pg_array([pg_json.call('a'=>1), pg_json.call(['b', 2])], json_type)
2847
- @ds.call(:insert, {:i=>j}, {:i=>:$i})
2848
- @ds.get(:i).must_equal j
2849
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
2850
-
2851
- it 'operations/functions with pg_json_ops' do
2852
- Sequel.extension :pg_json_ops
2853
- jo = pg_json.call('a'=>1, 'b'=>{'c'=>2, 'd'=>{'e'=>3}}).op
2854
- ja = pg_json.call([2, 3, %w'a b']).op
2855
-
2856
- @db.get(jo['a']).must_equal 1
2857
- @db.get(jo['b']['c']).must_equal 2
2858
- @db.get(jo[%w'b c']).must_equal 2
2859
- @db.get(jo['b'].get_text(%w'd e')).must_equal "3"
2860
- @db.get(jo[%w'b d'].get_text('e')).must_equal "3"
2861
- @db.get(ja[1]).must_equal 3
2862
- @db.get(ja[%w'2 1']).must_equal 'b'
2863
-
2864
- @db.get(jo.extract('a')).must_equal 1
2865
- @db.get(jo.extract('b').extract('c')).must_equal 2
2866
- @db.get(jo.extract('b', 'c')).must_equal 2
2867
- @db.get(jo.extract('b', 'd', 'e')).must_equal 3
2868
- @db.get(jo.extract_text('b', 'd')).gsub(' ', '').must_equal '{"e":3}'
2869
- @db.get(jo.extract_text('b', 'd', 'e')).must_equal '3'
2870
-
2871
- @db.get(ja.array_length).must_equal 3
2872
- @db.from(ja.array_elements.as(:v)).select_map(:v).must_equal [2, 3, %w'a b']
2873
-
2874
- if DB.server_version >= 90400
2875
- @db.get(jo.typeof).must_equal 'object'
2876
- @db.get(ja.typeof).must_equal 'array'
2877
- @db.from(ja.array_elements_text.as(:v)).select_map(:v).map{|s| s.gsub(' ', '')}.must_equal ['2', '3', '["a","b"]']
2878
- @db.from(jo.to_record.as(:v, [Sequel.lit('a integer'), Sequel.lit('b text')])).select_map(:a).must_equal [1]
2879
- @db.from(pg_json.call([{'a'=>1, 'b'=>1}]).op.to_recordset.as(:v, [Sequel.lit('a integer'), Sequel.lit('b integer')])).select_map(:a).must_equal [1]
2880
-
2881
- if json_type == :jsonb
2882
- @db.get(jo.has_key?('a')).must_equal true
2883
- @db.get(jo.has_key?('c')).must_equal false
2884
- @db.get(pg_json.call(['2', '3', %w'a b']).op.include?('2')).must_equal true
2885
- @db.get(pg_json.call(['2', '3', %w'a b']).op.include?('4')).must_equal false
2886
-
2887
- @db.get(jo.contain_all(['a', 'b'])).must_equal true
2888
- @db.get(jo.contain_all(['a', 'c'])).must_equal false
2889
- @db.get(jo.contain_all(['d', 'c'])).must_equal false
2890
- @db.get(jo.contain_any(['a', 'b'])).must_equal true
2891
- @db.get(jo.contain_any(['a', 'c'])).must_equal true
2892
- @db.get(jo.contain_any(['d', 'c'])).must_equal false
2893
-
2894
- @db.get(jo.contains(jo)).must_equal true
2895
- @db.get(jo.contained_by(jo)).must_equal true
2896
- @db.get(jo.contains('a'=>1)).must_equal true
2897
- @db.get(jo.contained_by('a'=>1)).must_equal false
2898
- @db.get(pg_json.call('a'=>1).op.contains(jo)).must_equal false
2899
- @db.get(pg_json.call('a'=>1).op.contained_by(jo)).must_equal true
2900
-
2901
- @db.get(ja.contains(ja)).must_equal true
2902
- @db.get(ja.contained_by(ja)).must_equal true
2903
- @db.get(ja.contains([2,3])).must_equal true
2904
- @db.get(ja.contained_by([2,3])).must_equal false
2905
- @db.get(pg_json.call([2,3]).op.contains(ja)).must_equal false
2906
- @db.get(pg_json.call([2,3]).op.contained_by(ja)).must_equal true
2907
- end
2908
- end
2909
-
2910
- if DB.server_version >= 90500 && json_type == :jsonb
2911
- @db.get(pg_json.call([nil, 2]).op.strip_nulls[1]).must_equal 2
2912
- @db.get(pg_json.call([nil, 2]).op.pretty).must_equal "[\n null,\n 2\n]"
2913
- @db.from((jo - 'b').keys.as(:k)).select_order_map(:k).must_equal %w'a'
2914
- @db.from(jo.delete_path(['b','c'])['b'].keys.as(:k)).select_order_map(:k).must_equal %w'd'
2915
- @db.from(jo.concat('c'=>'d').keys.as(:k)).select_order_map(:k).must_equal %w'a b c'
2916
- @db.get(jo.set(%w'a', 'f'=>'g')['a']['f']).must_equal 'g'
2917
- end
2918
-
2919
- @db.from(jo.keys.as(:k)).select_order_map(:k).must_equal %w'a b'
2920
- @db.from(jo.each).select_order_map(:key).must_equal %w'a b'
2921
- @db.from(jo.each).order(:key).select_map(:value).must_equal [1, {'c'=>2, 'd'=>{'e'=>3}}]
2922
- @db.from(jo.each_text).select_order_map(:key).must_equal %w'a b'
2923
- @db.from(jo.each_text).order(:key).where(:key=>'b').get(:value).gsub(' ', '').must_match(/\{"d":\{"e":3\},"c":2\}|\{"c":2,"d":\{"e":3\}\}/)
2924
-
2925
- Sequel.extension :pg_row_ops
2926
- @db.create_table!(:items) do
2927
- Integer :a
2928
- String :b
2929
- end
2930
- j = Sequel.pg_json('a'=>1, 'b'=>'c').op
2931
- @db.get(j.populate(Sequel.cast(nil, :items)).pg_row[:a]).must_equal 1
2932
- @db.get(j.populate(Sequel.cast(nil, :items)).pg_row[:b]).must_equal 'c'
2933
- j = Sequel.pg_json([{'a'=>1, 'b'=>'c'}, {'a'=>2, 'b'=>'d'}]).op
2934
- @db.from(j.populate_set(Sequel.cast(nil, :items))).select_order_map(:a).must_equal [1, 2]
2935
- @db.from(j.populate_set(Sequel.cast(nil, :items))).select_order_map(:b).must_equal %w'c d'
2936
- end if DB.server_version >= 90300 && (DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc)
2937
- end
2938
- end if DB.server_version >= 90200
2939
-
2940
- describe 'PostgreSQL inet/cidr types' do
2941
- ipv6_broken = (IPAddr.new('::1'); false) rescue true
2942
-
2943
- before(:all) do
2944
- @db = DB
2945
- @ds = @db[:items]
2946
- @v4 = '127.0.0.1'
2947
- @v4nm = '127.0.0.0/8'
2948
- @v6 = '2001:4f8:3:ba:2e0:81ff:fe22:d1f1'
2949
- @v6nm = '2001:4f8:3:ba::/64'
2950
- @ipv4 = IPAddr.new(@v4)
2951
- @ipv4nm = IPAddr.new(@v4nm)
2952
- unless ipv6_broken
2953
- @ipv6 = IPAddr.new(@v6)
2954
- @ipv6nm = IPAddr.new(@v6nm)
2955
- end
2956
- @native = DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
2957
- end
2958
- after do
2959
- @db.drop_table?(:items)
2960
- end
2961
-
2962
- it 'insert and retrieve inet/cidr values' do
2963
- @db.create_table!(:items){inet :i; cidr :c}
2964
- @ds.insert(@ipv4, @ipv4nm)
2965
- @ds.count.must_equal 1
2966
- if @native
2967
- rs = @ds.all
2968
- rs.first[:i].must_equal @ipv4
2969
- rs.first[:c].must_equal @ipv4nm
2970
- rs.first[:i].must_be_kind_of(IPAddr)
2971
- rs.first[:c].must_be_kind_of(IPAddr)
2972
- @ds.delete
2973
- @ds.insert(rs.first)
2974
- @ds.all.must_equal rs
2975
- end
2976
-
2977
- unless ipv6_broken
2978
- @ds.delete
2979
- @ds.insert(@ipv6, @ipv6nm)
2980
- @ds.count.must_equal 1
2981
- if @native
2982
- rs = @ds.all
2983
- rs.first[:j]
2984
- rs.first[:i].must_equal @ipv6
2985
- rs.first[:c].must_equal @ipv6nm
2986
- rs.first[:i].must_be_kind_of(IPAddr)
2987
- rs.first[:c].must_be_kind_of(IPAddr)
2988
- @ds.delete
2989
- @ds.insert(rs.first)
2990
- @ds.all.must_equal rs
2991
- end
2992
- end
2993
- end
2994
-
2995
- it 'insert and retrieve inet/cidr/macaddr array values' do
2996
- @db.create_table!(:items){column :i, 'inet[]'; column :c, 'cidr[]'; column :m, 'macaddr[]'}
2997
- @ds.insert(Sequel.pg_array([@ipv4], 'inet'), Sequel.pg_array([@ipv4nm], 'cidr'), Sequel.pg_array(['12:34:56:78:90:ab'], 'macaddr'))
2998
- @ds.count.must_equal 1
2999
- if @native
3000
- rs = @ds.all
3001
- rs.first.values.all?{|c| c.is_a?(Sequel::Postgres::PGArray)}.must_equal true
3002
- rs.first[:i].first.must_equal @ipv4
3003
- rs.first[:c].first.must_equal @ipv4nm
3004
- rs.first[:m].first.must_equal '12:34:56:78:90:ab'
3005
- rs.first[:i].first.must_be_kind_of(IPAddr)
3006
- rs.first[:c].first.must_be_kind_of(IPAddr)
3007
- @ds.delete
3008
- @ds.insert(rs.first)
3009
- @ds.all.must_equal rs
3010
- end
3011
- end
3012
-
3013
- it 'use ipaddr in bound variables' do
3014
- @db.create_table!(:items){inet :i; cidr :c}
3015
-
3016
- @ds.call(:insert, {:i=>@ipv4, :c=>@ipv4nm}, {:i=>:$i, :c=>:$c})
3017
- @ds.get(:i).must_equal @ipv4
3018
- @ds.get(:c).must_equal @ipv4nm
3019
- @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv4, :c=>@ipv4nm).must_equal(:i=>@ipv4, :c=>@ipv4nm)
3020
- @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv6, :c=>@ipv6nm).must_equal nil
3021
- @ds.filter(:i=>:$i, :c=>:$c).call(:delete, :i=>@ipv4, :c=>@ipv4nm).must_equal 1
3022
-
3023
- unless ipv6_broken
3024
- @ds.call(:insert, {:i=>@ipv6, :c=>@ipv6nm}, {:i=>:$i, :c=>:$c})
3025
- @ds.get(:i).must_equal @ipv6
3026
- @ds.get(:c).must_equal @ipv6nm
3027
- @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv6, :c=>@ipv6nm).must_equal(:i=>@ipv6, :c=>@ipv6nm)
3028
- @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv4, :c=>@ipv4nm).must_equal nil
3029
- @ds.filter(:i=>:$i, :c=>:$c).call(:delete, :i=>@ipv6, :c=>@ipv6nm).must_equal 1
3030
- end
3031
-
3032
- @db.create_table!(:items){column :i, 'inet[]'; column :c, 'cidr[]'; column :m, 'macaddr[]'}
3033
- @ds.call(:insert, {:i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']}, {:i=>:$i, :c=>:$c, :m=>:$m})
3034
- @ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:first, :i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']).must_equal(:i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab'])
3035
- @ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:first, :i=>[], :c=>[], :m=>[]).must_equal nil
3036
- @ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:delete, :i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']).must_equal 1
3037
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
3038
-
3039
- it 'with models' do
3040
- @db.create_table!(:items) do
3041
- primary_key :id
3042
- inet :i
3043
- cidr :c
3044
- end
3045
- c = Class.new(Sequel::Model(@db[:items]))
3046
- c.plugin :pg_typecast_on_load, :i, :c unless @native
3047
- c.create(:i=>@v4, :c=>@v4nm).values.values_at(:i, :c).must_equal [@ipv4, @ipv4nm]
3048
- unless ipv6_broken
3049
- c.create(:i=>@ipv6, :c=>@ipv6nm).values.values_at(:i, :c).must_equal [@ipv6, @ipv6nm]
3050
- end
3051
- end
3052
-
3053
- it 'operations/functions with pg_inet_ops' do
3054
- Sequel.extension :pg_inet_ops
3055
-
3056
- @db.get(Sequel.pg_inet_op('1.2.3.4') << '1.2.3.0/24').must_equal true
3057
- @db.get(Sequel.pg_inet_op('1.2.3.4') << '1.2.3.4/32').must_equal false
3058
- @db.get(Sequel.pg_inet_op('1.2.3.4') << '1.2.2.0/24').must_equal false
3059
- @db.get(Sequel.pg_inet_op('1.2.3.4').contained_by('1.2.3.0/24')).must_equal true
3060
- @db.get(Sequel.pg_inet_op('1.2.3.4').contained_by('1.2.3.4/32')).must_equal false
3061
- @db.get(Sequel.pg_inet_op('1.2.3.4').contained_by('1.2.2.0/24')).must_equal false
3062
-
3063
- @db.get(Sequel.pg_inet_op('1.2.3.4').contained_by_or_equals('1.2.3.0/24')).must_equal true
3064
- @db.get(Sequel.pg_inet_op('1.2.3.4').contained_by_or_equals('1.2.3.4/32')).must_equal true
3065
- @db.get(Sequel.pg_inet_op('1.2.3.4').contained_by_or_equals('1.2.2.0/24')).must_equal false
3066
-
3067
- @db.get(Sequel.pg_inet_op('1.2.3.0/24') >> '1.2.3.4').must_equal true
3068
- @db.get(Sequel.pg_inet_op('1.2.3.0/24') >> '1.2.2.4').must_equal false
3069
- @db.get(Sequel.pg_inet_op('1.2.3.0/24').contains('1.2.3.4')).must_equal true
3070
- @db.get(Sequel.pg_inet_op('1.2.3.0/24').contains('1.2.2.4')).must_equal false
3071
-
3072
- @db.get(Sequel.pg_inet_op('1.2.3.0/24').contains_or_equals('1.2.3.4')).must_equal true
3073
- @db.get(Sequel.pg_inet_op('1.2.3.0/24').contains_or_equals('1.2.2.4')).must_equal false
3074
- @db.get(Sequel.pg_inet_op('1.2.3.0/24').contains_or_equals('1.2.3.0/24')).must_equal true
3075
-
3076
- @db.get(Sequel.pg_inet_op('1.2.3.0/32') + 1).must_equal IPAddr.new('1.2.3.1/32')
3077
- @db.get(Sequel.pg_inet_op('1.2.3.1/32') - 1).must_equal IPAddr.new('1.2.3.0/32')
3078
- @db.get(Sequel.pg_inet_op('1.2.3.1/32') - '1.2.3.0/32').must_equal 1
3079
- @db.get(Sequel.pg_inet_op('1.2.3.0/32') & '1.2.0.4/32').must_equal IPAddr.new('1.2.0.0/32')
3080
- @db.get(Sequel.pg_inet_op('1.2.0.0/32') | '0.0.3.4/32').must_equal IPAddr.new('1.2.3.4/32')
3081
- @db.get(~Sequel.pg_inet_op('0.0.0.0/32')).must_equal IPAddr.new('255.255.255.255/32')
3082
-
3083
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').abbrev).must_equal '1.2.3.4/24'
3084
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').broadcast).must_equal IPAddr.new('1.2.3.255/24')
3085
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').family).must_equal 4
3086
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').host).must_equal '1.2.3.4'
3087
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').hostmask).must_equal IPAddr.new('0.0.0.255/32')
3088
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').masklen).must_equal 24
3089
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').netmask).must_equal IPAddr.new('255.255.255.0/32')
3090
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').network).must_equal IPAddr.new('1.2.3.0/24')
3091
- @db.get(Sequel.pg_inet_op('1.2.3.4/24').set_masklen(16)).must_equal IPAddr.new('1.2.3.4/16')
3092
- @db.get(Sequel.pg_inet_op('1.2.3.4/32').text).must_equal '1.2.3.4/32'
3093
-
3094
- if @db.server_version >= 90400
3095
- @db.get(Sequel.pg_inet_op('1.2.3.0/24').contains_or_contained_by('1.2.0.0/16')).must_equal true
3096
- @db.get(Sequel.pg_inet_op('1.2.0.0/16').contains_or_contained_by('1.2.3.0/24')).must_equal true
3097
- @db.get(Sequel.pg_inet_op('1.3.0.0/16').contains_or_contained_by('1.2.3.0/24')).must_equal false
3098
- end
3099
- end
3100
- end
3101
-
3102
- describe 'PostgreSQL custom range types' do
3103
- after do
3104
- @db.run "DROP TYPE timerange";
3105
- end
3106
-
3107
- it "should allow registration and use" do
3108
- @db = DB
3109
- @db.run "CREATE TYPE timerange AS range (subtype = time)"
3110
- @db.register_range_type('timerange')
3111
- r = Sequel::SQLTime.create(10, 11, 12)..Sequel::SQLTime.create(11, 12, 13)
3112
- @db.get(Sequel.pg_range(r, :timerange)).to_range.must_equal r
3113
- end
3114
- end if DB.server_version >= 90200 && DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
3115
-
3116
- describe 'PostgreSQL range types' do
3117
- before(:all) do
3118
- @db = DB
3119
- @ds = @db[:items]
3120
- @map = {:i4=>'int4range', :i8=>'int8range', :n=>'numrange', :d=>'daterange', :t=>'tsrange', :tz=>'tstzrange'}
3121
- @r = {:i4=>1...2, :i8=>2...3, :n=>BigDecimal.new('1.0')..BigDecimal.new('2.0'), :d=>Date.today...(Date.today+1), :t=>Time.local(2011, 1)..Time.local(2011, 2), :tz=>Time.local(2011, 1)..Time.local(2011, 2)}
3122
- @ra = {}
3123
- @pgr = {}
3124
- @pgra = {}
3125
- @r.each{|k, v| @ra[k] = Sequel.pg_array([v], @map[k])}
3126
- @r.each{|k, v| @pgr[k] = Sequel.pg_range(v)}
3127
- @r.each{|k, v| @pgra[k] = Sequel.pg_array([Sequel.pg_range(v)], @map[k])}
3128
- @native = DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
3129
- end
3130
- after do
3131
- @db.drop_table?(:items)
3132
- end
3133
-
3134
- it 'insert and retrieve range type values' do
3135
- @db.create_table!(:items){int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
3136
- [@r, @pgr].each do |input|
3137
- h = {}
3138
- input.each{|k, v| h[k] = Sequel.cast(v, @map[k])}
3139
- @ds.insert(h)
3140
- @ds.count.must_equal 1
3141
- if @native
3142
- rs = @ds.all
3143
- rs.first.each do |k, v|
3144
- v.class.must_equal(Sequel::Postgres::PGRange)
3145
- v.to_range.must_be_kind_of(Range)
3146
- v.must_be :==, @r[k]
3147
- v.to_range.must_equal @r[k]
3148
- end
3149
- @ds.delete
3150
- @ds.insert(rs.first)
3151
- @ds.all.must_equal rs
3152
- end
3153
- @ds.delete
3154
- end
3155
- end
3156
-
3157
- it 'insert and retrieve arrays of range type values' do
3158
- @db.create_table!(:items){column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
3159
- [@ra, @pgra].each do |input|
3160
- @ds.insert(input)
3161
- @ds.count.must_equal 1
3162
- if @native
3163
- rs = @ds.all
3164
- rs.first.each do |k, v|
3165
- v.class.must_equal(Sequel::Postgres::PGArray)
3166
- v.to_a.must_be_kind_of(Array)
3167
- v.first.class.must_equal(Sequel::Postgres::PGRange)
3168
- v.first.to_range.must_be_kind_of(Range)
3169
- v.must_be :==, @ra[k].to_a
3170
- v.first.must_be :==, @r[k]
3171
- end
3172
- @ds.delete
3173
- @ds.insert(rs.first)
3174
- @ds.all.must_equal rs
3175
- end
3176
- @ds.delete
3177
- end
3178
- end
3179
-
3180
- it 'use range types in bound variables' do
3181
- @db.create_table!(:items){int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
3182
- h = {}
3183
- @r.keys.each{|k| h[k] = :"$#{k}"}
3184
- r2 = {}
3185
- @r.each{|k, v| r2[k] = Range.new(v.begin, v.end+2)}
3186
- @ds.call(:insert, @r, h)
3187
- @ds.first.must_be :==, @r
3188
- @ds.filter(h).call(:first, @r).must_be :==, @r
3189
- @ds.filter(h).call(:first, @pgr).must_be :==, @r
3190
- @ds.filter(h).call(:first, r2).must_equal nil
3191
- @ds.filter(h).call(:delete, @r).must_equal 1
3192
-
3193
- @db.create_table!(:items){column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
3194
- @r.each{|k, v| r2[k] = [Range.new(v.begin, v.end+2)]}
3195
- @ds.call(:insert, @ra, h)
3196
- @ds.filter(h).call(:first, @ra).each{|k, v| v.must_be :==, @ra[k].to_a}
3197
- @ds.filter(h).call(:first, @pgra).each{|k, v| v.must_be :==, @ra[k].to_a}
3198
- @ds.filter(h).call(:first, r2).must_equal nil
3199
- @ds.filter(h).call(:delete, @ra).must_equal 1
3200
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
3201
-
3202
- it 'with models' do
3203
- @db.create_table!(:items){primary_key :id; int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
3204
- c = Class.new(Sequel::Model(@db[:items]))
3205
- c.plugin :pg_typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
3206
- v = c.create(@r).values
3207
- v.delete(:id)
3208
- v.must_be :==, @r
3209
-
3210
- @db.create_table!(:items){primary_key :id; column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
3211
- c = Class.new(Sequel::Model(@db[:items]))
3212
- c.plugin :pg_typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
3213
- v = c.create(@ra).values
3214
- v.delete(:id)
3215
- v.each{|k,v1| v1.must_be :==, @ra[k].to_a}
3216
- end
3217
-
3218
- it 'works with current_datetime_timestamp extension' do
3219
- ds = @db.dataset.extension(:current_datetime_timestamp)
3220
- tsr = ds.get(Sequel.pg_range(ds.current_datetime..ds.current_datetime, :tstzrange))
3221
- if @native
3222
- tsr.begin.must_be_kind_of Time
3223
- tsr.end.must_be_kind_of Time
3224
- end
3225
- end
3226
-
3227
- it 'operations/functions with pg_range_ops' do
3228
- Sequel.extension :pg_range_ops
3229
-
3230
- @db.get(Sequel.pg_range(1..5, :int4range).op.contains(2..4)).must_equal true
3231
- @db.get(Sequel.pg_range(1..5, :int4range).op.contains(3..6)).must_equal false
3232
- @db.get(Sequel.pg_range(1..5, :int4range).op.contains(0..6)).must_equal false
3233
-
3234
- @db.get(Sequel.pg_range(1..5, :int4range).op.contained_by(0..6)).must_equal true
3235
- @db.get(Sequel.pg_range(1..5, :int4range).op.contained_by(3..6)).must_equal false
3236
- @db.get(Sequel.pg_range(1..5, :int4range).op.contained_by(2..4)).must_equal false
3237
-
3238
- @db.get(Sequel.pg_range(1..5, :int4range).op.overlaps(5..6)).must_equal true
3239
- @db.get(Sequel.pg_range(1...5, :int4range).op.overlaps(5..6)).must_equal false
3240
-
3241
- @db.get(Sequel.pg_range(1..5, :int4range).op.left_of(6..10)).must_equal true
3242
- @db.get(Sequel.pg_range(1..5, :int4range).op.left_of(5..10)).must_equal false
3243
- @db.get(Sequel.pg_range(1..5, :int4range).op.left_of(-1..0)).must_equal false
3244
- @db.get(Sequel.pg_range(1..5, :int4range).op.left_of(-1..3)).must_equal false
3245
-
3246
- @db.get(Sequel.pg_range(1..5, :int4range).op.right_of(6..10)).must_equal false
3247
- @db.get(Sequel.pg_range(1..5, :int4range).op.right_of(5..10)).must_equal false
3248
- @db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..0)).must_equal true
3249
- @db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..3)).must_equal false
3250
-
3251
- @db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(6..10)).must_equal true
3252
- @db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(5..10)).must_equal true
3253
- @db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..0)).must_equal false
3254
- @db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..3)).must_equal false
3255
- @db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..7)).must_equal true
3256
-
3257
- @db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(6..10)).must_equal false
3258
- @db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(5..10)).must_equal false
3259
- @db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(3..10)).must_equal false
3260
- @db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..10)).must_equal true
3261
- @db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..0)).must_equal true
3262
- @db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..3)).must_equal true
3263
- @db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-5..-1)).must_equal true
3264
-
3265
- @db.get(Sequel.pg_range(1..5, :int4range).op.adjacent_to(6..10)).must_equal true
3266
- @db.get(Sequel.pg_range(1...5, :int4range).op.adjacent_to(6..10)).must_equal false
3267
-
3268
- @db.get((Sequel.pg_range(1..5, :int4range).op + (6..10)).adjacent_to(6..10)).must_equal false
3269
- @db.get((Sequel.pg_range(1..5, :int4range).op + (6..10)).adjacent_to(11..20)).must_equal true
3270
-
3271
- @db.get((Sequel.pg_range(1..5, :int4range).op * (2..6)).adjacent_to(6..10)).must_equal true
3272
- @db.get((Sequel.pg_range(1..4, :int4range).op * (2..6)).adjacent_to(6..10)).must_equal false
3273
-
3274
- @db.get((Sequel.pg_range(1..5, :int4range).op - (2..6)).adjacent_to(2..10)).must_equal true
3275
- @db.get((Sequel.pg_range(0..4, :int4range).op - (3..6)).adjacent_to(4..10)).must_equal false
3276
-
3277
- @db.get(Sequel.pg_range(0..4, :int4range).op.lower).must_equal 0
3278
- @db.get(Sequel.pg_range(0..4, :int4range).op.upper).must_equal 5
3279
-
3280
- @db.get(Sequel.pg_range(0..4, :int4range).op.isempty).must_equal false
3281
- @db.get(Sequel::Postgres::PGRange.empty(:int4range).op.isempty).must_equal true
3282
-
3283
- @db.get(Sequel.pg_range(1..5, :numrange).op.lower_inc).must_equal true
3284
- @db.get(Sequel::Postgres::PGRange.new(1, 5, :exclude_begin=>true, :db_type=>:numrange).op.lower_inc).must_equal false
3285
-
3286
- @db.get(Sequel.pg_range(1..5, :numrange).op.upper_inc).must_equal true
3287
- @db.get(Sequel.pg_range(1...5, :numrange).op.upper_inc).must_equal false
3288
-
3289
- @db.get(Sequel::Postgres::PGRange.new(1, 5, :db_type=>:int4range).op.lower_inf).must_equal false
3290
- @db.get(Sequel::Postgres::PGRange.new(nil, 5, :db_type=>:int4range).op.lower_inf).must_equal true
3291
-
3292
- @db.get(Sequel::Postgres::PGRange.new(1, 5, :db_type=>:int4range).op.upper_inf).must_equal false
3293
- @db.get(Sequel::Postgres::PGRange.new(1, nil, :db_type=>:int4range).op.upper_inf).must_equal true
3294
- end
3295
- end if DB.server_version >= 90200
3296
-
3297
- describe 'PostgreSQL interval types' do
3298
- before(:all) do
3299
- @db = DB
3300
- @ds = @db[:items]
3301
- @native = DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
3302
- end
3303
- after(:all) do
3304
- Sequel::Postgres::PG_TYPES.delete(1186)
3305
- end
3306
- after do
3307
- @db.drop_table?(:items)
3308
- end
3309
-
3310
- it 'insert and retrieve interval values' do
3311
- @db.create_table!(:items){interval :i}
3312
- [
3313
- ['0', '00:00:00', 0, []],
3314
- ['1', '00:00:01', 1, [[:seconds, 1]]],
3315
- ['1 microsecond', '00:00:00.000001', 0.000001, [[:seconds, 0.000001]]],
3316
- ['1 millisecond', '00:00:00.001', 0.001, [[:seconds, 0.001]]],
3317
- ['1 second', '00:00:01', 1, [[:seconds, 1]]],
3318
- ['1 minute', '00:01:00', 60, [[:seconds, 60]]],
3319
- ['1 hour', '01:00:00', 3600, [[:seconds, 3600]]],
3320
- ['123000 hours', '123000:00:00', 442800000, [[:seconds, 442800000]]],
3321
- ['1 day', '1 day', 86400, [[:days, 1]]],
3322
- ['1 week', '7 days', 86400*7, [[:days, 7]]],
3323
- ['1 month', '1 mon', 86400*30, [[:months, 1]]],
3324
- ['1 year', '1 year', 31557600, [[:years, 1]]],
3325
- ['1 decade', '10 years', 31557600*10, [[:years, 10]]],
3326
- ['1 century', '100 years', 31557600*100, [[:years, 100]]],
3327
- ['1 millennium', '1000 years', 31557600*1000, [[:years, 1000]]],
3328
- ['1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', '1 year 2 mons 25 days 05:06:07', 31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]]],
3329
- ['-1 year +2 months -3 weeks +4 days -5 hours +6 minutes -7 seconds', '-10 mons -17 days -04:54:07', -10*86400*30 - 3*86400*7 + 4*86400 - 5*3600 + 6*60 - 7, [[:months, -10], [:days, -17], [:seconds, -17647]]],
3330
- ['+2 years -1 months +3 weeks -4 days +5 hours -6 minutes +7 seconds', '1 year 11 mons 17 days 04:54:07', 31557600 + 11*86400*30 + 3*86400*7 - 4*86400 + 5*3600 - 6*60 + 7, [[:years, 1], [:months, 11], [:days, 17], [:seconds, 17647]]],
3331
- ].each do |instr, outstr, value, parts|
3332
- @ds.insert(instr)
3333
- @ds.count.must_equal 1
3334
- if @native
3335
- @ds.get(Sequel.cast(:i, String)).must_equal outstr
3336
- rs = @ds.all
3337
- rs.first[:i].is_a?(ActiveSupport::Duration).must_equal true
3338
- rs.first[:i].must_equal ActiveSupport::Duration.new(value, parts)
3339
- rs.first[:i].parts.sort_by{|k,v| k.to_s}.reject{|k,v| v == 0}.must_equal parts.sort_by{|k,v| k.to_s}
3340
- @ds.delete
3341
- @ds.insert(rs.first)
3342
- @ds.all.must_equal rs
3343
- end
3344
- @ds.delete
3345
- end
3346
- end
3347
-
3348
- it 'insert and retrieve interval array values' do
3349
- @db.create_table!(:items){column :i, 'interval[]'}
3350
- @ds.insert(Sequel.pg_array(['1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds'], 'interval'))
3351
- @ds.count.must_equal 1
3352
- if @native
3353
- rs = @ds.all
3354
- rs.first[:i].is_a?(Sequel::Postgres::PGArray).must_equal true
3355
- rs.first[:i].first.is_a?(ActiveSupport::Duration).must_equal true
3356
- rs.first[:i].first.must_equal ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
3357
- rs.first[:i].first.parts.sort_by{|k,v| k.to_s}.must_equal [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,v| k.to_s}
3358
- @ds.delete
3359
- @ds.insert(rs.first)
3360
- @ds.all.must_equal rs
3361
- end
3362
- end
3363
-
3364
- it 'use intervals in bound variables' do
3365
- @db.create_table!(:items){interval :i}
3366
- @ds.insert('1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds')
3367
- d = @ds.get(:i)
3368
- @ds.delete
3369
-
3370
- @ds.call(:insert, {:i=>d}, {:i=>:$i})
3371
- @ds.get(:i).must_equal d
3372
- @ds.filter(:i=>:$i).call(:first, :i=>d).must_equal(:i=>d)
3373
- @ds.filter(:i=>Sequel.cast(:$i, :interval)).call(:first, :i=>'0').must_equal nil
3374
- @ds.filter(:i=>:$i).call(:delete, :i=>d).must_equal 1
3375
-
3376
- @db.create_table!(:items){column :i, 'interval[]'}
3377
- @ds.call(:insert, {:i=>[d]}, {:i=>:$i})
3378
- @ds.filter(:i=>:$i).call(:first, :i=>[d]).must_equal(:i=>[d])
3379
- @ds.filter(:i=>:$i).call(:first, :i=>[]).must_equal nil
3380
- @ds.filter(:i=>:$i).call(:delete, :i=>[d]).must_equal 1
3381
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
3382
-
3383
- it 'with models' do
3384
- @db.create_table!(:items) do
3385
- primary_key :id
3386
- interval :i
3387
- end
3388
- c = Class.new(Sequel::Model(@db[:items]))
3389
- c.plugin :pg_typecast_on_load, :i, :c unless @native
3390
- v = c.create(:i=>'1 year 2 mons 25 days 05:06:07').i
3391
- v.is_a?(ActiveSupport::Duration).must_equal true
3392
- v.must_equal ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
3393
- v.parts.sort_by{|k,_| k.to_s}.must_equal [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,_| k.to_s}
3394
- end
3395
- end if (begin require 'active_support/duration'; require 'active_support/inflector'; require 'active_support/core_ext/string/inflections'; true; rescue LoadError; false end)
3396
-
3397
- describe 'PostgreSQL row-valued/composite types' do
3398
- before(:all) do
3399
- @db = DB
3400
- Sequel.extension :pg_array_ops, :pg_row_ops
3401
- @ds = @db[:person]
3402
-
3403
- @db.create_table!(:address) do
3404
- String :street
3405
- String :city
3406
- String :zip
3407
- end
3408
- @db.create_table!(:person) do
3409
- Integer :id
3410
- address :address
3411
- end
3412
- @db.create_table!(:company) do
3413
- Integer :id
3414
- column :employees, 'person[]'
3415
- end
3416
- @db.register_row_type(:address)
3417
- @db.register_row_type(Sequel.qualify(:public, :person))
3418
- @db.register_row_type(:public__company)
3419
-
3420
- @native = DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
3421
- end
3422
- after(:all) do
3423
- @db.drop_table?(:company, :person, :address)
3424
- @db.row_types.clear
3425
- @db.reset_conversion_procs if @native
3426
- end
3427
- after do
3428
- [:company, :person, :address].each{|t| @db[t].delete}
3429
- end
3430
-
3431
- it 'insert and retrieve row types' do
3432
- @ds.insert(:id=>1, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345']))
3433
- @ds.count.must_equal 1
3434
- if @native
3435
- # Single row valued type
3436
- rs = @ds.all
3437
- v = rs.first[:address]
3438
- v.class.superclass.must_equal(Sequel::Postgres::PGRow::HashRow)
3439
- v.to_hash.must_be_kind_of(Hash)
3440
- v.to_hash.must_equal(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345')
3441
- @ds.delete
3442
- @ds.insert(rs.first)
3443
- @ds.all.must_equal rs
3444
-
3445
- # Nested row value type
3446
- p = @ds.get(:person)
3447
- p[:id].must_equal 1
3448
- p[:address].must_equal v
3449
- end
3450
- end
3451
-
3452
- it 'insert and retrieve row types containing domains' do
3453
- begin
3454
- @db << "DROP DOMAIN IF EXISTS positive_integer CASCADE"
3455
- @db << "CREATE DOMAIN positive_integer AS integer CHECK (VALUE > 0)"
3456
- @db.create_table!(:domain_check) do
3457
- positive_integer :id
3458
- end
3459
- @db.register_row_type(:domain_check)
3460
- @db.get(@db.row_type(:domain_check, [1])).must_equal(:id=>1)
3461
- @db.register_row_type(:public__domain_check)
3462
- @db.get(@db.row_type(:public__domain_check, [1])).must_equal(:id=>1)
3463
- @db.get(@db.row_type(Sequel.qualify(:public, :domain_check), [1])).must_equal(:id=>1)
3464
- ensure
3465
- @db.drop_table(:domain_check)
3466
- @db << "DROP DOMAIN positive_integer"
3467
- end
3468
- end if DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
3469
-
3470
- it 'insert and retrieve arrays of row types' do
3471
- @ds = @db[:company]
3472
- @ds.insert(:id=>1, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))
3473
- @ds.count.must_equal 1
3474
- if @native
3475
- v = @ds.get(:company)
3476
- v.class.superclass.must_equal(Sequel::Postgres::PGRow::HashRow)
3477
- v.to_hash.must_be_kind_of(Hash)
3478
- v[:id].must_equal 1
3479
- employees = v[:employees]
3480
- employees.class.must_equal(Sequel::Postgres::PGArray)
3481
- employees.to_a.must_be_kind_of(Array)
3482
- employees.must_equal [{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}]
3483
- @ds.delete
3484
- @ds.insert(v[:id], v[:employees])
3485
- @ds.get(:company).must_equal v
3486
- end
3487
- end
3488
-
3489
- it 'use row types in bound variables' do
3490
- @ds.call(:insert, {:address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])}, {:address=>:$address, :id=>1})
3491
- @ds.get(:address).must_equal(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345')
3492
- @ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345']))[:id].must_equal 1
3493
- @ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])).must_equal nil
3494
-
3495
- @ds.delete
3496
- @ds.call(:insert, {:address=>Sequel.pg_row([nil, nil, nil])}, {:address=>:$address, :id=>1})
3497
- @ds.get(:address).must_equal(:street=>nil, :city=>nil, :zip=>nil)
3498
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
3499
-
3500
- it 'use arrays of row types in bound variables' do
3501
- @ds = @db[:company]
3502
- @ds.call(:insert, {:employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])])}, {:employees=>:$employees, :id=>1})
3503
- @ds.get(:company).must_equal(:id=>1, :employees=>[{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}])
3504
- @ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))[:id].must_equal 1
3505
- @ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])])])).must_equal nil
3506
-
3507
- @ds.delete
3508
- @ds.call(:insert, {:employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row([nil, nil, nil])])])}, {:employees=>:$employees, :id=>1})
3509
- @ds.get(:employees).must_equal [{:address=>{:city=>nil, :zip=>nil, :street=>nil}, :id=>1}]
3510
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
3511
-
3512
- it 'operations/functions with pg_row_ops' do
3513
- @ds.insert(:id=>1, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345']))
3514
- @ds.get(Sequel.pg_row(:address)[:street]).must_equal '123 Sesame St'
3515
- @ds.get(Sequel.pg_row(:address)[:city]).must_equal 'Somewhere'
3516
- @ds.get(Sequel.pg_row(:address)[:zip]).must_equal '12345'
3517
-
3518
- @ds = @db[:company]
3519
- @ds.insert(:id=>1, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))
3520
- @ds.get(Sequel.pg_row(:company)[:id]).must_equal 1
3521
- if @native
3522
- @ds.get(Sequel.pg_row(:company)[:employees]).must_equal [{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}]
3523
- @ds.get(Sequel.pg_row(:company)[:employees][1]).must_equal(:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'})
3524
- @ds.get(Sequel.pg_row(:company)[:employees][1][:address]).must_equal(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345')
3525
- end
3526
- @ds.get(Sequel.pg_row(:company)[:employees][1][:id]).must_equal 1
3527
- @ds.get(Sequel.pg_row(:company)[:employees][1][:address][:street]).must_equal '123 Sesame St'
3528
- @ds.get(Sequel.pg_row(:company)[:employees][1][:address][:city]).must_equal 'Somewhere'
3529
- @ds.get(Sequel.pg_row(:company)[:employees][1][:address][:zip]).must_equal '12345'
3530
- end
3531
-
3532
- describe "#splat and #*" do
3533
- before(:all) do
3534
- @db.create_table!(:a){Integer :a}
3535
- @db.create_table!(:b){a :b; Integer :a}
3536
- @db.register_row_type(:a)
3537
- @db.register_row_type(:b)
3538
- @db[:b].insert(:a=>1, :b=>@db.row_type(:a, [2]))
3539
- end
3540
- after(:all) do
3541
- @db.drop_table?(:b, :a)
3542
- end
3543
-
3544
- it "splat should reference the table type" do
3545
- @db[:b].select(:a).first.must_equal(:a=>1)
3546
- @db[:b].select(:b__a).first.must_equal(:a=>1)
3547
- @db[:b].select(Sequel.pg_row(:b)[:a]).first.must_equal(:a=>2)
3548
- @db[:b].select(Sequel.pg_row(:b).splat[:a]).first.must_equal(:a=>1)
3549
-
3550
- if @native
3551
- @db[:b].select(:b).first.must_equal(:b=>{:a=>2})
3552
- @db[:b].select(Sequel.pg_row(:b).splat).first.must_equal(:a=>1, :b=>{:a=>2})
3553
- @db[:b].select(Sequel.pg_row(:b).splat(:b)).first.must_equal(:b=>{:a=>1, :b=>{:a=>2}})
3554
- end
3555
- end
3556
-
3557
- it "* should expand the table type into separate columns" do
3558
- ds = @db[:b].select(Sequel.pg_row(:b).splat(:b)).from_self(:alias=>:t)
3559
- if @native
3560
- ds.first.must_equal(:b=>{:a=>1, :b=>{:a=>2}})
3561
- ds.select(Sequel.pg_row(:b).*).first.must_equal(:a=>1, :b=>{:a=>2})
3562
- ds.select(Sequel.pg_row(:b)[:b]).first.must_equal(:b=>{:a=>2})
3563
- ds.select(Sequel.pg_row(:t__b).*).first.must_equal(:a=>1, :b=>{:a=>2})
3564
- ds.select(Sequel.pg_row(:t__b)[:b]).first.must_equal(:b=>{:a=>2})
3565
- end
3566
- ds.select(Sequel.pg_row(:b)[:a]).first.must_equal(:a=>1)
3567
- ds.select(Sequel.pg_row(:t__b)[:a]).first.must_equal(:a=>1)
3568
- end
3569
- end
3570
-
3571
- describe "with models" do
3572
- before(:all) do
3573
- class Address < Sequel::Model(:address)
3574
- plugin :pg_row
3575
- end
3576
- class Person < Sequel::Model(:person)
3577
- plugin :pg_row
3578
- end
3579
- class Company < Sequel::Model(:company)
3580
- plugin :pg_row
3581
- end
3582
- @a = Address.new(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345')
3583
- @es = Sequel.pg_array([Person.new(:id=>1, :address=>@a)])
3584
- end
3585
- after(:all) do
3586
- Object.send(:remove_const, :Address) rescue nil
3587
- Object.send(:remove_const, :Person) rescue nil
3588
- Object.send(:remove_const, :Company) rescue nil
3589
- end
3590
-
3591
- it 'insert and retrieve row types as model objects' do
3592
- @ds.insert(:id=>1, :address=>@a)
3593
- @ds.count.must_equal 1
3594
- if @native
3595
- # Single row valued type
3596
- rs = @ds.all
3597
- v = rs.first[:address]
3598
- v.must_be_kind_of(Address)
3599
- v.must_equal @a
3600
- @ds.delete
3601
- @ds.insert(rs.first)
3602
- @ds.all.must_equal rs
3603
-
3604
- # Nested row value type
3605
- p = @ds.get(:person)
3606
- p.must_be_kind_of(Person)
3607
- p.id.must_equal 1
3608
- p.address.must_be_kind_of(Address)
3609
- p.address.must_equal @a
3610
- end
3611
- end
3612
-
3613
- it 'insert and retrieve arrays of row types as model objects' do
3614
- @ds = @db[:company]
3615
- @ds.insert(:id=>1, :employees=>@es)
3616
- @ds.count.must_equal 1
3617
- if @native
3618
- v = @ds.get(:company)
3619
- v.must_be_kind_of(Company)
3620
- v.id.must_equal 1
3621
- employees = v[:employees]
3622
- employees.class.must_equal(Sequel::Postgres::PGArray)
3623
- employees.to_a.must_be_kind_of(Array)
3624
- employees.must_equal @es
3625
- @ds.delete
3626
- @ds.insert(v.id, v.employees)
3627
- @ds.get(:company).must_equal v
3628
- end
3629
- end
3630
-
3631
- it 'use model objects in bound variables' do
3632
- @ds.call(:insert, {:address=>@a}, {:address=>:$address, :id=>1})
3633
- @ds.get(:address).must_equal @a
3634
- @ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>@a)[:id].must_equal 1
3635
- @ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>Address.new(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12356')).must_equal nil
3636
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
3637
-
3638
- it 'use arrays of model objects in bound variables' do
3639
- @ds = @db[:company]
3640
- @ds.call(:insert, {:employees=>@es}, {:employees=>:$employees, :id=>1})
3641
- @ds.get(:company).must_equal Company.new(:id=>1, :employees=>@es)
3642
- @ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>@es)[:id].must_equal 1
3643
- @ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])])])).must_equal nil
3644
- end if (DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG) || DB.adapter_scheme == :jdbc
3645
-
3646
- it 'model typecasting' do
3647
- Person.plugin :pg_typecast_on_load, :address unless @native
3648
- a = Address.new(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345')
3649
- o = Person.create(:id=>1, :address=>['123 Sesame St', 'Somewhere', '12345'])
3650
- o.address.must_equal a
3651
- o = Person.create(:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'})
3652
- o.address.must_equal a
3653
- o = Person.create(:id=>1, :address=>a)
3654
- o.address.must_equal a
3655
-
3656
- Company.plugin :pg_typecast_on_load, :employees unless @native
3657
- e = Person.new(:id=>1, :address=>a)
3658
- o = Company.create(:id=>1, :employees=>[{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}])
3659
- o.employees.must_equal [e]
3660
- o = Company.create(:id=>1, :employees=>[e])
3661
- o.employees.must_equal [e]
3662
- end
3663
- end
3664
- end
3665
-
3666
- describe 'pg_static_cache_updater extension' do
3667
- before(:all) do
3668
- @db = DB
3669
- @db.extension :pg_static_cache_updater
3670
- @db.drop_function(@db.default_static_cache_update_name, :cascade=>true, :if_exists=>true)
3671
- @db.create_static_cache_update_function
3672
-
3673
- @db.create_table!(:things) do
3674
- primary_key :id
3675
- String :name
3676
- end
3677
- @Thing = Class.new(Sequel::Model(:things))
3678
- @Thing.plugin :static_cache
3679
- @db.create_static_cache_update_trigger(:things)
3680
- end
3681
- after(:all) do
3682
- @db.drop_table(:things)
3683
- @db.drop_function(@db.default_static_cache_update_name)
3684
- end
3685
-
3686
- it "should reload model static cache when underlying table changes" do
3687
- @Thing.all.must_equal []
3688
- q = Queue.new
3689
- q1 = Queue.new
3690
-
3691
- @db.listen_for_static_cache_updates(@Thing, :timeout=>0, :loop=>proc{q.push(nil); q1.pop.call}, :before_thread_exit=>proc{q.push(nil)})
3692
-
3693
- q.pop
3694
- q1.push(proc{@db[:things].insert(1, 'A')})
3695
- q.pop
3696
- @Thing.all.must_equal [@Thing.load(:id=>1, :name=>'A')]
3697
-
3698
- q1.push(proc{@db[:things].update(:name=>'B')})
3699
- q.pop
3700
- @Thing.all.must_equal [@Thing.load(:id=>1, :name=>'B')]
3701
-
3702
- q1.push(proc{@db[:things].delete})
3703
- q.pop
3704
- @Thing.all.must_equal []
3705
-
3706
- q1.push(proc{throw :stop})
3707
- q.pop
3708
- end
3709
- end if DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && DB.server_version >= 90000
3710
-
3711
- describe 'PostgreSQL enum types' do
3712
- before do
3713
- @db = DB
3714
- @db.create_enum(:test_enum, %w'a b c d')
3715
-
3716
- @db.create_table!(:test_enumt) do
3717
- test_enum :t
3718
- end
3719
- end
3720
- after do
3721
- @db.drop_table?(:test_enumt)
3722
- @db.drop_enum(:test_enum)
3723
- end
3724
-
3725
- it "should return correct entries in the schema" do
3726
- s = @db.schema(:test_enumt)
3727
- s.first.last[:type].must_equal :enum
3728
- s.first.last[:enum_values].must_equal %w'a b c d'
3729
- end
3730
-
3731
- it "should add array parsers for enum values" do
3732
- @db.get(Sequel.pg_array(%w'a b', :test_enum)).must_equal %w'a b'
3733
- end if DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
3734
-
3735
- it "should set up model typecasting correctly" do
3736
- c = Class.new(Sequel::Model(:test_enumt))
3737
- o = c.new
3738
- o.t = :a
3739
- o.t.must_equal 'a'
3740
- end
3741
-
3742
- it "should add values to existing enum" do
3743
- @db.add_enum_value(:test_enum, 'e')
3744
- @db.add_enum_value(:test_enum, 'f', :after=>'a')
3745
- @db.add_enum_value(:test_enum, 'g', :before=>'b')
3746
- @db.add_enum_value(:test_enum, 'a', :if_not_exists=>true) if @db.server_version >= 90300
3747
- @db.schema(:test_enumt, :reload=>true).first.last[:enum_values].must_equal %w'a f g b c d e'
3748
- end if DB.server_version >= 90100
3749
- end
3750
-
3751
- describe "PostgreSQL stored procedures for datasets" do
3752
- before do
3753
- require 'sequel/adapters/utils/stored_procedures'
3754
-
3755
- @db = DB
3756
- @db.create_table!(:items) do
3757
- primary_key :id
3758
- integer :numb
3759
- end
3760
- @db.execute(<<-SQL)
3761
- create or replace function insert_item(numb bigint)
3762
- returns items.id%type
3763
- as $$
3764
- declare
3765
- l_id items.id%type;
3766
- begin
3767
- l_id := 1;
3768
-
3769
- insert into items(id, numb) values(l_id, numb);
3770
-
3771
- return l_id;
3772
- end;
3773
- $$ language plpgsql;
3774
- SQL
3775
-
3776
- @ds = @db[:items]
3777
- end
3778
-
3779
- after do
3780
- @db.drop_function("insert_item", :if_exists=>true)
3781
- @db.drop_table?(:items)
3782
- end
3783
-
3784
- it "should correctly call stored procedure for inserting record" do
3785
- result = @ds.call_sproc(:insert, :insert_item, 100)
3786
- result.must_equal nil
3787
-
3788
- @ds.call(:all).must_equal [{:id=>1, :numb=>100}]
3789
- end
3790
- end if DB.adapter_scheme == :jdbc