sequel 4.26.0 → 5.37.0

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