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