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