sequel 4.36.0 → 5.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (760) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG +548 -5749
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +265 -159
  5. data/bin/sequel +34 -12
  6. data/doc/advanced_associations.rdoc +228 -187
  7. data/doc/association_basics.rdoc +281 -291
  8. data/doc/bin_sequel.rdoc +5 -3
  9. data/doc/cheat_sheet.rdoc +86 -51
  10. data/doc/code_order.rdoc +25 -19
  11. data/doc/core_extensions.rdoc +104 -63
  12. data/doc/dataset_basics.rdoc +12 -21
  13. data/doc/dataset_filtering.rdoc +99 -86
  14. data/doc/extensions.rdoc +3 -10
  15. data/doc/fork_safety.rdoc +84 -0
  16. data/doc/mass_assignment.rdoc +74 -31
  17. data/doc/migration.rdoc +59 -51
  18. data/doc/model_dataset_method_design.rdoc +129 -0
  19. data/doc/model_hooks.rdoc +15 -25
  20. data/doc/model_plugins.rdoc +12 -12
  21. data/doc/mssql_stored_procedures.rdoc +3 -3
  22. data/doc/object_model.rdoc +58 -68
  23. data/doc/opening_databases.rdoc +85 -95
  24. data/doc/postgresql.rdoc +263 -38
  25. data/doc/prepared_statements.rdoc +29 -24
  26. data/doc/querying.rdoc +189 -167
  27. data/doc/reflection.rdoc +5 -6
  28. data/doc/release_notes/5.0.0.txt +159 -0
  29. data/doc/release_notes/5.1.0.txt +31 -0
  30. data/doc/release_notes/5.10.0.txt +84 -0
  31. data/doc/release_notes/5.11.0.txt +83 -0
  32. data/doc/release_notes/5.12.0.txt +141 -0
  33. data/doc/release_notes/5.13.0.txt +27 -0
  34. data/doc/release_notes/5.14.0.txt +63 -0
  35. data/doc/release_notes/5.15.0.txt +39 -0
  36. data/doc/release_notes/5.16.0.txt +110 -0
  37. data/doc/release_notes/5.17.0.txt +31 -0
  38. data/doc/release_notes/5.18.0.txt +69 -0
  39. data/doc/release_notes/5.19.0.txt +28 -0
  40. data/doc/release_notes/5.2.0.txt +33 -0
  41. data/doc/release_notes/5.20.0.txt +89 -0
  42. data/doc/release_notes/5.21.0.txt +87 -0
  43. data/doc/release_notes/5.22.0.txt +48 -0
  44. data/doc/release_notes/5.23.0.txt +56 -0
  45. data/doc/release_notes/5.24.0.txt +56 -0
  46. data/doc/release_notes/5.25.0.txt +32 -0
  47. data/doc/release_notes/5.26.0.txt +35 -0
  48. data/doc/release_notes/5.27.0.txt +21 -0
  49. data/doc/release_notes/5.28.0.txt +16 -0
  50. data/doc/release_notes/5.29.0.txt +22 -0
  51. data/doc/release_notes/5.3.0.txt +121 -0
  52. data/doc/release_notes/5.30.0.txt +20 -0
  53. data/doc/release_notes/5.31.0.txt +148 -0
  54. data/doc/release_notes/5.32.0.txt +46 -0
  55. data/doc/release_notes/5.33.0.txt +24 -0
  56. data/doc/release_notes/5.34.0.txt +40 -0
  57. data/doc/release_notes/5.35.0.txt +56 -0
  58. data/doc/release_notes/5.36.0.txt +60 -0
  59. data/doc/release_notes/5.37.0.txt +30 -0
  60. data/doc/release_notes/5.38.0.txt +28 -0
  61. data/doc/release_notes/5.39.0.txt +19 -0
  62. data/doc/release_notes/5.4.0.txt +80 -0
  63. data/doc/release_notes/5.40.0.txt +40 -0
  64. data/doc/release_notes/5.41.0.txt +25 -0
  65. data/doc/release_notes/5.42.0.txt +136 -0
  66. data/doc/release_notes/5.43.0.txt +98 -0
  67. data/doc/release_notes/5.44.0.txt +32 -0
  68. data/doc/release_notes/5.45.0.txt +34 -0
  69. data/doc/release_notes/5.46.0.txt +87 -0
  70. data/doc/release_notes/5.47.0.txt +59 -0
  71. data/doc/release_notes/5.48.0.txt +14 -0
  72. data/doc/release_notes/5.49.0.txt +59 -0
  73. data/doc/release_notes/5.5.0.txt +61 -0
  74. data/doc/release_notes/5.50.0.txt +78 -0
  75. data/doc/release_notes/5.51.0.txt +47 -0
  76. data/doc/release_notes/5.52.0.txt +87 -0
  77. data/doc/release_notes/5.53.0.txt +23 -0
  78. data/doc/release_notes/5.54.0.txt +27 -0
  79. data/doc/release_notes/5.55.0.txt +21 -0
  80. data/doc/release_notes/5.56.0.txt +51 -0
  81. data/doc/release_notes/5.57.0.txt +23 -0
  82. data/doc/release_notes/5.58.0.txt +31 -0
  83. data/doc/release_notes/5.59.0.txt +73 -0
  84. data/doc/release_notes/5.6.0.txt +31 -0
  85. data/doc/release_notes/5.60.0.txt +22 -0
  86. data/doc/release_notes/5.61.0.txt +43 -0
  87. data/doc/release_notes/5.7.0.txt +108 -0
  88. data/doc/release_notes/5.8.0.txt +170 -0
  89. data/doc/release_notes/5.9.0.txt +99 -0
  90. data/doc/schema_modification.rdoc +95 -75
  91. data/doc/security.rdoc +109 -80
  92. data/doc/sharding.rdoc +74 -47
  93. data/doc/sql.rdoc +147 -122
  94. data/doc/testing.rdoc +43 -20
  95. data/doc/thread_safety.rdoc +2 -4
  96. data/doc/transactions.rdoc +97 -18
  97. data/doc/validations.rdoc +52 -50
  98. data/doc/virtual_rows.rdoc +90 -109
  99. data/lib/sequel/adapters/ado/access.rb +15 -17
  100. data/lib/sequel/adapters/ado/mssql.rb +6 -15
  101. data/lib/sequel/adapters/ado.rb +150 -20
  102. data/lib/sequel/adapters/amalgalite.rb +11 -23
  103. data/lib/sequel/adapters/ibmdb.rb +47 -55
  104. data/lib/sequel/adapters/jdbc/db2.rb +29 -39
  105. data/lib/sequel/adapters/jdbc/derby.rb +58 -54
  106. data/lib/sequel/adapters/jdbc/h2.rb +93 -35
  107. data/lib/sequel/adapters/jdbc/hsqldb.rb +24 -31
  108. data/lib/sequel/adapters/jdbc/jtds.rb +2 -10
  109. data/lib/sequel/adapters/jdbc/mssql.rb +3 -11
  110. data/lib/sequel/adapters/jdbc/mysql.rb +17 -20
  111. data/lib/sequel/adapters/jdbc/oracle.rb +22 -18
  112. data/lib/sequel/adapters/jdbc/postgresql.rb +69 -71
  113. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +11 -23
  114. data/lib/sequel/adapters/jdbc/sqlite.rb +47 -11
  115. data/lib/sequel/adapters/jdbc/sqlserver.rb +34 -9
  116. data/lib/sequel/adapters/jdbc/transactions.rb +22 -38
  117. data/lib/sequel/adapters/jdbc.rb +145 -130
  118. data/lib/sequel/adapters/mock.rb +100 -111
  119. data/lib/sequel/adapters/mysql.rb +114 -122
  120. data/lib/sequel/adapters/mysql2.rb +147 -63
  121. data/lib/sequel/adapters/odbc/db2.rb +1 -1
  122. data/lib/sequel/adapters/odbc/mssql.rb +8 -14
  123. data/lib/sequel/adapters/odbc/oracle.rb +11 -0
  124. data/lib/sequel/adapters/odbc.rb +20 -25
  125. data/lib/sequel/adapters/oracle.rb +50 -56
  126. data/lib/sequel/adapters/postgres.rb +305 -327
  127. data/lib/sequel/adapters/postgresql.rb +1 -1
  128. data/lib/sequel/adapters/shared/access.rb +74 -78
  129. data/lib/sequel/adapters/shared/db2.rb +118 -71
  130. data/lib/sequel/adapters/shared/mssql.rb +301 -220
  131. data/lib/sequel/adapters/shared/mysql.rb +299 -217
  132. data/lib/sequel/adapters/shared/oracle.rb +226 -65
  133. data/lib/sequel/adapters/shared/postgres.rb +935 -395
  134. data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -126
  135. data/lib/sequel/adapters/shared/sqlite.rb +447 -173
  136. data/lib/sequel/adapters/sqlanywhere.rb +48 -35
  137. data/lib/sequel/adapters/sqlite.rb +156 -111
  138. data/lib/sequel/adapters/tinytds.rb +30 -38
  139. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  140. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +3 -6
  141. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -2
  142. data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
  143. data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
  144. data/lib/sequel/adapters/utils/replace.rb +1 -4
  145. data/lib/sequel/adapters/utils/stored_procedures.rb +7 -22
  146. data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
  147. data/lib/sequel/ast_transformer.rb +17 -89
  148. data/lib/sequel/connection_pool/sharded_single.rb +18 -15
  149. data/lib/sequel/connection_pool/sharded_threaded.rb +130 -111
  150. data/lib/sequel/connection_pool/single.rb +18 -13
  151. data/lib/sequel/connection_pool/threaded.rb +121 -120
  152. data/lib/sequel/connection_pool.rb +48 -29
  153. data/lib/sequel/core.rb +351 -301
  154. data/lib/sequel/database/connecting.rb +69 -57
  155. data/lib/sequel/database/dataset.rb +13 -5
  156. data/lib/sequel/database/dataset_defaults.rb +18 -102
  157. data/lib/sequel/database/features.rb +18 -4
  158. data/lib/sequel/database/logging.rb +12 -11
  159. data/lib/sequel/database/misc.rb +180 -122
  160. data/lib/sequel/database/query.rb +47 -27
  161. data/lib/sequel/database/schema_generator.rb +178 -84
  162. data/lib/sequel/database/schema_methods.rb +172 -97
  163. data/lib/sequel/database/transactions.rb +205 -44
  164. data/lib/sequel/database.rb +17 -2
  165. data/lib/sequel/dataset/actions.rb +339 -155
  166. data/lib/sequel/dataset/dataset_module.rb +46 -0
  167. data/lib/sequel/dataset/features.rb +90 -35
  168. data/lib/sequel/dataset/graph.rb +80 -58
  169. data/lib/sequel/dataset/misc.rb +137 -47
  170. data/lib/sequel/dataset/placeholder_literalizer.rb +63 -25
  171. data/lib/sequel/dataset/prepared_statements.rb +188 -85
  172. data/lib/sequel/dataset/query.rb +530 -222
  173. data/lib/sequel/dataset/sql.rb +590 -368
  174. data/lib/sequel/dataset.rb +26 -16
  175. data/lib/sequel/deprecated.rb +12 -2
  176. data/lib/sequel/exceptions.rb +46 -16
  177. data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
  178. data/lib/sequel/extensions/_model_pg_row.rb +43 -0
  179. data/lib/sequel/extensions/_pretty_table.rb +2 -5
  180. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  181. data/lib/sequel/extensions/arbitrary_servers.rb +10 -10
  182. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  183. data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
  184. data/lib/sequel/extensions/blank.rb +8 -0
  185. data/lib/sequel/extensions/caller_logging.rb +79 -0
  186. data/lib/sequel/extensions/columns_introspection.rb +4 -3
  187. data/lib/sequel/extensions/connection_expiration.rb +20 -10
  188. data/lib/sequel/extensions/connection_validator.rb +11 -10
  189. data/lib/sequel/extensions/constant_sql_override.rb +65 -0
  190. data/lib/sequel/extensions/constraint_validations.rb +62 -39
  191. data/lib/sequel/extensions/core_extensions.rb +42 -48
  192. data/lib/sequel/extensions/core_refinements.rb +80 -59
  193. data/lib/sequel/extensions/current_datetime_timestamp.rb +1 -4
  194. data/lib/sequel/extensions/date_arithmetic.rb +98 -39
  195. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  196. data/lib/sequel/extensions/datetime_parse_to_time.rb +41 -0
  197. data/lib/sequel/extensions/duplicate_columns_handler.rb +21 -14
  198. data/lib/sequel/extensions/empty_array_consider_nulls.rb +2 -2
  199. data/lib/sequel/extensions/escaped_like.rb +100 -0
  200. data/lib/sequel/extensions/eval_inspect.rb +12 -15
  201. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  202. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  203. data/lib/sequel/extensions/freeze_datasets.rb +3 -0
  204. data/lib/sequel/extensions/from_block.rb +1 -34
  205. data/lib/sequel/extensions/graph_each.rb +4 -4
  206. data/lib/sequel/extensions/identifier_mangling.rb +180 -0
  207. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  208. data/lib/sequel/extensions/index_caching.rb +109 -0
  209. data/lib/sequel/extensions/inflector.rb +13 -5
  210. data/lib/sequel/extensions/integer64.rb +32 -0
  211. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  212. data/lib/sequel/extensions/looser_typecasting.rb +17 -8
  213. data/lib/sequel/extensions/migration.rb +119 -78
  214. data/lib/sequel/extensions/named_timezones.rb +88 -23
  215. data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -82
  216. data/lib/sequel/extensions/null_dataset.rb +8 -8
  217. data/lib/sequel/extensions/pagination.rb +32 -29
  218. data/lib/sequel/extensions/pg_array.rb +221 -287
  219. data/lib/sequel/extensions/pg_array_ops.rb +17 -9
  220. data/lib/sequel/extensions/pg_enum.rb +63 -23
  221. data/lib/sequel/extensions/pg_extended_date_support.rb +241 -0
  222. data/lib/sequel/extensions/pg_hstore.rb +45 -54
  223. data/lib/sequel/extensions/pg_hstore_ops.rb +58 -6
  224. data/lib/sequel/extensions/pg_inet.rb +31 -12
  225. data/lib/sequel/extensions/pg_inet_ops.rb +2 -2
  226. data/lib/sequel/extensions/pg_interval.rb +56 -29
  227. data/lib/sequel/extensions/pg_json.rb +417 -140
  228. data/lib/sequel/extensions/pg_json_ops.rb +270 -18
  229. data/lib/sequel/extensions/pg_loose_count.rb +4 -2
  230. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  231. data/lib/sequel/extensions/pg_range.rb +131 -191
  232. data/lib/sequel/extensions/pg_range_ops.rb +42 -13
  233. data/lib/sequel/extensions/pg_row.rb +48 -81
  234. data/lib/sequel/extensions/pg_row_ops.rb +33 -14
  235. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
  236. data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
  237. data/lib/sequel/extensions/query.rb +9 -7
  238. data/lib/sequel/extensions/round_timestamps.rb +0 -6
  239. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  240. data/lib/sequel/extensions/s.rb +60 -0
  241. data/lib/sequel/extensions/schema_caching.rb +10 -1
  242. data/lib/sequel/extensions/schema_dumper.rb +71 -48
  243. data/lib/sequel/extensions/select_remove.rb +4 -4
  244. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
  245. data/lib/sequel/extensions/server_block.rb +51 -27
  246. data/lib/sequel/extensions/split_array_nil.rb +4 -4
  247. data/lib/sequel/extensions/sql_comments.rb +119 -7
  248. data/lib/sequel/extensions/sql_expr.rb +2 -1
  249. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  250. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  251. data/lib/sequel/extensions/string_agg.rb +11 -8
  252. data/lib/sequel/extensions/string_date_time.rb +19 -23
  253. data/lib/sequel/extensions/symbol_aref.rb +55 -0
  254. data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
  255. data/lib/sequel/extensions/symbol_as.rb +23 -0
  256. data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
  257. data/lib/sequel/extensions/synchronize_sql.rb +45 -0
  258. data/lib/sequel/extensions/to_dot.rb +10 -4
  259. data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
  260. data/lib/sequel/model/associations.rb +1006 -284
  261. data/lib/sequel/model/base.rb +560 -805
  262. data/lib/sequel/model/dataset_module.rb +11 -10
  263. data/lib/sequel/model/default_inflections.rb +1 -1
  264. data/lib/sequel/model/errors.rb +10 -3
  265. data/lib/sequel/model/exceptions.rb +8 -10
  266. data/lib/sequel/model/inflections.rb +7 -20
  267. data/lib/sequel/model/plugins.rb +114 -0
  268. data/lib/sequel/model.rb +32 -82
  269. data/lib/sequel/plugins/active_model.rb +30 -14
  270. data/lib/sequel/plugins/after_initialize.rb +1 -1
  271. data/lib/sequel/plugins/association_dependencies.rb +25 -18
  272. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  273. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  274. data/lib/sequel/plugins/association_pks.rb +147 -70
  275. data/lib/sequel/plugins/association_proxies.rb +33 -9
  276. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  277. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  278. data/lib/sequel/plugins/auto_validations.rb +95 -28
  279. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  280. data/lib/sequel/plugins/before_after_save.rb +0 -42
  281. data/lib/sequel/plugins/blacklist_security.rb +21 -12
  282. data/lib/sequel/plugins/boolean_readers.rb +5 -5
  283. data/lib/sequel/plugins/boolean_subsets.rb +13 -8
  284. data/lib/sequel/plugins/caching.rb +25 -16
  285. data/lib/sequel/plugins/class_table_inheritance.rb +179 -100
  286. data/lib/sequel/plugins/column_conflicts.rb +16 -3
  287. data/lib/sequel/plugins/column_encryption.rb +728 -0
  288. data/lib/sequel/plugins/column_select.rb +7 -5
  289. data/lib/sequel/plugins/columns_updated.rb +42 -0
  290. data/lib/sequel/plugins/composition.rb +42 -26
  291. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  292. data/lib/sequel/plugins/constraint_validations.rb +20 -14
  293. data/lib/sequel/plugins/csv_serializer.rb +56 -35
  294. data/lib/sequel/plugins/dataset_associations.rb +40 -17
  295. data/lib/sequel/plugins/def_dataset_method.rb +90 -0
  296. data/lib/sequel/plugins/defaults_setter.rb +65 -10
  297. data/lib/sequel/plugins/delay_add_association.rb +1 -1
  298. data/lib/sequel/plugins/dirty.rb +62 -24
  299. data/lib/sequel/plugins/eager_each.rb +3 -3
  300. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  301. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  302. data/lib/sequel/plugins/enum.rb +124 -0
  303. data/lib/sequel/plugins/error_splitter.rb +17 -12
  304. data/lib/sequel/plugins/finder.rb +246 -0
  305. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  306. data/lib/sequel/plugins/force_encoding.rb +7 -12
  307. data/lib/sequel/plugins/hook_class_methods.rb +37 -54
  308. data/lib/sequel/plugins/input_transformer.rb +18 -10
  309. data/lib/sequel/plugins/insert_conflict.rb +76 -0
  310. data/lib/sequel/plugins/insert_returning_select.rb +2 -2
  311. data/lib/sequel/plugins/instance_filters.rb +10 -8
  312. data/lib/sequel/plugins/instance_hooks.rb +34 -17
  313. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  314. data/lib/sequel/plugins/inverted_subsets.rb +22 -13
  315. data/lib/sequel/plugins/json_serializer.rb +124 -64
  316. data/lib/sequel/plugins/lazy_attributes.rb +21 -14
  317. data/lib/sequel/plugins/list.rb +35 -21
  318. data/lib/sequel/plugins/many_through_many.rb +134 -21
  319. data/lib/sequel/plugins/modification_detection.rb +15 -5
  320. data/lib/sequel/plugins/mssql_optimistic_locking.rb +6 -5
  321. data/lib/sequel/plugins/nested_attributes.rb +61 -31
  322. data/lib/sequel/plugins/optimistic_locking.rb +3 -3
  323. data/lib/sequel/plugins/pg_array_associations.rb +103 -53
  324. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +350 -0
  325. data/lib/sequel/plugins/pg_row.rb +5 -51
  326. data/lib/sequel/plugins/prepared_statements.rb +60 -72
  327. data/lib/sequel/plugins/prepared_statements_safe.rb +9 -4
  328. data/lib/sequel/plugins/rcte_tree.rb +68 -82
  329. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  330. data/lib/sequel/plugins/serialization.rb +43 -46
  331. data/lib/sequel/plugins/serialization_modification_detection.rb +3 -2
  332. data/lib/sequel/plugins/sharding.rb +15 -10
  333. data/lib/sequel/plugins/single_table_inheritance.rb +67 -28
  334. data/lib/sequel/plugins/skip_create_refresh.rb +3 -3
  335. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  336. data/lib/sequel/plugins/split_values.rb +11 -6
  337. data/lib/sequel/plugins/sql_comments.rb +189 -0
  338. data/lib/sequel/plugins/static_cache.rb +77 -53
  339. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  340. data/lib/sequel/plugins/string_stripper.rb +3 -3
  341. data/lib/sequel/plugins/subclasses.rb +43 -10
  342. data/lib/sequel/plugins/subset_conditions.rb +15 -5
  343. data/lib/sequel/plugins/table_select.rb +2 -2
  344. data/lib/sequel/plugins/tactical_eager_loading.rb +96 -12
  345. data/lib/sequel/plugins/throw_failures.rb +110 -0
  346. data/lib/sequel/plugins/timestamps.rb +20 -8
  347. data/lib/sequel/plugins/touch.rb +19 -8
  348. data/lib/sequel/plugins/tree.rb +62 -32
  349. data/lib/sequel/plugins/typecast_on_load.rb +12 -4
  350. data/lib/sequel/plugins/unlimited_update.rb +1 -7
  351. data/lib/sequel/plugins/unused_associations.rb +521 -0
  352. data/lib/sequel/plugins/update_or_create.rb +4 -4
  353. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  354. data/lib/sequel/plugins/update_refresh.rb +26 -15
  355. data/lib/sequel/plugins/uuid.rb +7 -11
  356. data/lib/sequel/plugins/validate_associated.rb +18 -0
  357. data/lib/sequel/plugins/validation_class_methods.rb +38 -19
  358. data/lib/sequel/plugins/validation_contexts.rb +49 -0
  359. data/lib/sequel/plugins/validation_helpers.rb +57 -41
  360. data/lib/sequel/plugins/whitelist_security.rb +122 -0
  361. data/lib/sequel/plugins/xml_serializer.rb +30 -31
  362. data/lib/sequel/sql.rb +471 -331
  363. data/lib/sequel/timezones.rb +78 -47
  364. data/lib/sequel/version.rb +7 -2
  365. data/lib/sequel.rb +1 -1
  366. metadata +217 -521
  367. data/Rakefile +0 -164
  368. data/doc/active_record.rdoc +0 -928
  369. data/doc/release_notes/1.0.txt +0 -38
  370. data/doc/release_notes/1.1.txt +0 -143
  371. data/doc/release_notes/1.3.txt +0 -101
  372. data/doc/release_notes/1.4.0.txt +0 -53
  373. data/doc/release_notes/1.5.0.txt +0 -155
  374. data/doc/release_notes/2.0.0.txt +0 -298
  375. data/doc/release_notes/2.1.0.txt +0 -271
  376. data/doc/release_notes/2.10.0.txt +0 -328
  377. data/doc/release_notes/2.11.0.txt +0 -215
  378. data/doc/release_notes/2.12.0.txt +0 -534
  379. data/doc/release_notes/2.2.0.txt +0 -253
  380. data/doc/release_notes/2.3.0.txt +0 -88
  381. data/doc/release_notes/2.4.0.txt +0 -106
  382. data/doc/release_notes/2.5.0.txt +0 -137
  383. data/doc/release_notes/2.6.0.txt +0 -157
  384. data/doc/release_notes/2.7.0.txt +0 -166
  385. data/doc/release_notes/2.8.0.txt +0 -171
  386. data/doc/release_notes/2.9.0.txt +0 -97
  387. data/doc/release_notes/3.0.0.txt +0 -221
  388. data/doc/release_notes/3.1.0.txt +0 -406
  389. data/doc/release_notes/3.10.0.txt +0 -286
  390. data/doc/release_notes/3.11.0.txt +0 -254
  391. data/doc/release_notes/3.12.0.txt +0 -304
  392. data/doc/release_notes/3.13.0.txt +0 -210
  393. data/doc/release_notes/3.14.0.txt +0 -118
  394. data/doc/release_notes/3.15.0.txt +0 -78
  395. data/doc/release_notes/3.16.0.txt +0 -45
  396. data/doc/release_notes/3.17.0.txt +0 -58
  397. data/doc/release_notes/3.18.0.txt +0 -120
  398. data/doc/release_notes/3.19.0.txt +0 -67
  399. data/doc/release_notes/3.2.0.txt +0 -268
  400. data/doc/release_notes/3.20.0.txt +0 -41
  401. data/doc/release_notes/3.21.0.txt +0 -87
  402. data/doc/release_notes/3.22.0.txt +0 -39
  403. data/doc/release_notes/3.23.0.txt +0 -172
  404. data/doc/release_notes/3.24.0.txt +0 -420
  405. data/doc/release_notes/3.25.0.txt +0 -88
  406. data/doc/release_notes/3.26.0.txt +0 -88
  407. data/doc/release_notes/3.27.0.txt +0 -82
  408. data/doc/release_notes/3.28.0.txt +0 -304
  409. data/doc/release_notes/3.29.0.txt +0 -459
  410. data/doc/release_notes/3.3.0.txt +0 -192
  411. data/doc/release_notes/3.30.0.txt +0 -135
  412. data/doc/release_notes/3.31.0.txt +0 -146
  413. data/doc/release_notes/3.32.0.txt +0 -202
  414. data/doc/release_notes/3.33.0.txt +0 -157
  415. data/doc/release_notes/3.34.0.txt +0 -671
  416. data/doc/release_notes/3.35.0.txt +0 -144
  417. data/doc/release_notes/3.36.0.txt +0 -245
  418. data/doc/release_notes/3.37.0.txt +0 -338
  419. data/doc/release_notes/3.38.0.txt +0 -234
  420. data/doc/release_notes/3.39.0.txt +0 -237
  421. data/doc/release_notes/3.4.0.txt +0 -325
  422. data/doc/release_notes/3.40.0.txt +0 -73
  423. data/doc/release_notes/3.41.0.txt +0 -155
  424. data/doc/release_notes/3.42.0.txt +0 -74
  425. data/doc/release_notes/3.43.0.txt +0 -105
  426. data/doc/release_notes/3.44.0.txt +0 -152
  427. data/doc/release_notes/3.45.0.txt +0 -179
  428. data/doc/release_notes/3.46.0.txt +0 -122
  429. data/doc/release_notes/3.47.0.txt +0 -270
  430. data/doc/release_notes/3.48.0.txt +0 -477
  431. data/doc/release_notes/3.5.0.txt +0 -510
  432. data/doc/release_notes/3.6.0.txt +0 -366
  433. data/doc/release_notes/3.7.0.txt +0 -179
  434. data/doc/release_notes/3.8.0.txt +0 -151
  435. data/doc/release_notes/3.9.0.txt +0 -233
  436. data/doc/release_notes/4.0.0.txt +0 -262
  437. data/doc/release_notes/4.1.0.txt +0 -85
  438. data/doc/release_notes/4.10.0.txt +0 -226
  439. data/doc/release_notes/4.11.0.txt +0 -147
  440. data/doc/release_notes/4.12.0.txt +0 -105
  441. data/doc/release_notes/4.13.0.txt +0 -169
  442. data/doc/release_notes/4.14.0.txt +0 -68
  443. data/doc/release_notes/4.15.0.txt +0 -56
  444. data/doc/release_notes/4.16.0.txt +0 -36
  445. data/doc/release_notes/4.17.0.txt +0 -38
  446. data/doc/release_notes/4.18.0.txt +0 -36
  447. data/doc/release_notes/4.19.0.txt +0 -45
  448. data/doc/release_notes/4.2.0.txt +0 -129
  449. data/doc/release_notes/4.20.0.txt +0 -79
  450. data/doc/release_notes/4.21.0.txt +0 -94
  451. data/doc/release_notes/4.22.0.txt +0 -72
  452. data/doc/release_notes/4.23.0.txt +0 -65
  453. data/doc/release_notes/4.24.0.txt +0 -99
  454. data/doc/release_notes/4.25.0.txt +0 -181
  455. data/doc/release_notes/4.26.0.txt +0 -44
  456. data/doc/release_notes/4.27.0.txt +0 -78
  457. data/doc/release_notes/4.28.0.txt +0 -57
  458. data/doc/release_notes/4.29.0.txt +0 -41
  459. data/doc/release_notes/4.3.0.txt +0 -40
  460. data/doc/release_notes/4.30.0.txt +0 -37
  461. data/doc/release_notes/4.31.0.txt +0 -57
  462. data/doc/release_notes/4.32.0.txt +0 -132
  463. data/doc/release_notes/4.33.0.txt +0 -88
  464. data/doc/release_notes/4.34.0.txt +0 -86
  465. data/doc/release_notes/4.35.0.txt +0 -130
  466. data/doc/release_notes/4.36.0.txt +0 -116
  467. data/doc/release_notes/4.4.0.txt +0 -92
  468. data/doc/release_notes/4.5.0.txt +0 -34
  469. data/doc/release_notes/4.6.0.txt +0 -30
  470. data/doc/release_notes/4.7.0.txt +0 -103
  471. data/doc/release_notes/4.8.0.txt +0 -175
  472. data/doc/release_notes/4.9.0.txt +0 -190
  473. data/lib/sequel/adapters/cubrid.rb +0 -144
  474. data/lib/sequel/adapters/do/mysql.rb +0 -66
  475. data/lib/sequel/adapters/do/postgres.rb +0 -44
  476. data/lib/sequel/adapters/do/sqlite3.rb +0 -42
  477. data/lib/sequel/adapters/do.rb +0 -158
  478. data/lib/sequel/adapters/jdbc/as400.rb +0 -84
  479. data/lib/sequel/adapters/jdbc/cubrid.rb +0 -64
  480. data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -36
  481. data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -33
  482. data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -33
  483. data/lib/sequel/adapters/odbc/progress.rb +0 -10
  484. data/lib/sequel/adapters/shared/cubrid.rb +0 -245
  485. data/lib/sequel/adapters/shared/firebird.rb +0 -247
  486. data/lib/sequel/adapters/shared/informix.rb +0 -54
  487. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -152
  488. data/lib/sequel/adapters/shared/progress.rb +0 -40
  489. data/lib/sequel/adapters/swift/mysql.rb +0 -49
  490. data/lib/sequel/adapters/swift/postgres.rb +0 -47
  491. data/lib/sequel/adapters/swift/sqlite.rb +0 -49
  492. data/lib/sequel/adapters/swift.rb +0 -160
  493. data/lib/sequel/adapters/utils/pg_types.rb +0 -70
  494. data/lib/sequel/dataset/mutation.rb +0 -111
  495. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -5
  496. data/lib/sequel/extensions/filter_having.rb +0 -63
  497. data/lib/sequel/extensions/hash_aliases.rb +0 -49
  498. data/lib/sequel/extensions/meta_def.rb +0 -35
  499. data/lib/sequel/extensions/query_literals.rb +0 -84
  500. data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -24
  501. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -122
  502. data/lib/sequel/extensions/set_overrides.rb +0 -76
  503. data/lib/sequel/no_core_ext.rb +0 -3
  504. data/lib/sequel/plugins/association_autoreloading.rb +0 -9
  505. data/lib/sequel/plugins/identifier_columns.rb +0 -47
  506. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -9
  507. data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -81
  508. data/lib/sequel/plugins/prepared_statements_associations.rb +0 -119
  509. data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -61
  510. data/lib/sequel/plugins/schema.rb +0 -82
  511. data/lib/sequel/plugins/scissors.rb +0 -35
  512. data/spec/adapter_spec.rb +0 -4
  513. data/spec/adapters/db2_spec.rb +0 -160
  514. data/spec/adapters/firebird_spec.rb +0 -411
  515. data/spec/adapters/informix_spec.rb +0 -100
  516. data/spec/adapters/mssql_spec.rb +0 -733
  517. data/spec/adapters/mysql_spec.rb +0 -1319
  518. data/spec/adapters/oracle_spec.rb +0 -313
  519. data/spec/adapters/postgres_spec.rb +0 -3790
  520. data/spec/adapters/spec_helper.rb +0 -49
  521. data/spec/adapters/sqlanywhere_spec.rb +0 -170
  522. data/spec/adapters/sqlite_spec.rb +0 -688
  523. data/spec/bin_spec.rb +0 -258
  524. data/spec/core/connection_pool_spec.rb +0 -1045
  525. data/spec/core/database_spec.rb +0 -2636
  526. data/spec/core/dataset_spec.rb +0 -5175
  527. data/spec/core/deprecated_spec.rb +0 -70
  528. data/spec/core/expression_filters_spec.rb +0 -1247
  529. data/spec/core/mock_adapter_spec.rb +0 -464
  530. data/spec/core/object_graph_spec.rb +0 -303
  531. data/spec/core/placeholder_literalizer_spec.rb +0 -163
  532. data/spec/core/schema_generator_spec.rb +0 -203
  533. data/spec/core/schema_spec.rb +0 -1676
  534. data/spec/core/spec_helper.rb +0 -34
  535. data/spec/core/version_spec.rb +0 -7
  536. data/spec/core_extensions_spec.rb +0 -699
  537. data/spec/core_model_spec.rb +0 -2
  538. data/spec/core_spec.rb +0 -1
  539. data/spec/extensions/accessed_columns_spec.rb +0 -51
  540. data/spec/extensions/active_model_spec.rb +0 -85
  541. data/spec/extensions/after_initialize_spec.rb +0 -24
  542. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  543. data/spec/extensions/association_dependencies_spec.rb +0 -117
  544. data/spec/extensions/association_pks_spec.rb +0 -405
  545. data/spec/extensions/association_proxies_spec.rb +0 -86
  546. data/spec/extensions/auto_validations_spec.rb +0 -192
  547. data/spec/extensions/before_after_save_spec.rb +0 -40
  548. data/spec/extensions/blacklist_security_spec.rb +0 -88
  549. data/spec/extensions/blank_spec.rb +0 -69
  550. data/spec/extensions/boolean_readers_spec.rb +0 -93
  551. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  552. data/spec/extensions/caching_spec.rb +0 -270
  553. data/spec/extensions/class_table_inheritance_spec.rb +0 -444
  554. data/spec/extensions/column_conflicts_spec.rb +0 -60
  555. data/spec/extensions/column_select_spec.rb +0 -108
  556. data/spec/extensions/columns_introspection_spec.rb +0 -91
  557. data/spec/extensions/composition_spec.rb +0 -242
  558. data/spec/extensions/connection_expiration_spec.rb +0 -121
  559. data/spec/extensions/connection_validator_spec.rb +0 -127
  560. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -288
  561. data/spec/extensions/constraint_validations_spec.rb +0 -389
  562. data/spec/extensions/core_refinements_spec.rb +0 -519
  563. data/spec/extensions/csv_serializer_spec.rb +0 -180
  564. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  565. data/spec/extensions/dataset_associations_spec.rb +0 -343
  566. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  567. data/spec/extensions/date_arithmetic_spec.rb +0 -167
  568. data/spec/extensions/defaults_setter_spec.rb +0 -102
  569. data/spec/extensions/delay_add_association_spec.rb +0 -74
  570. data/spec/extensions/dirty_spec.rb +0 -180
  571. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -110
  572. data/spec/extensions/eager_each_spec.rb +0 -66
  573. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  574. data/spec/extensions/error_splitter_spec.rb +0 -18
  575. data/spec/extensions/error_sql_spec.rb +0 -20
  576. data/spec/extensions/eval_inspect_spec.rb +0 -73
  577. data/spec/extensions/filter_having_spec.rb +0 -40
  578. data/spec/extensions/force_encoding_spec.rb +0 -114
  579. data/spec/extensions/from_block_spec.rb +0 -21
  580. data/spec/extensions/graph_each_spec.rb +0 -119
  581. data/spec/extensions/hash_aliases_spec.rb +0 -24
  582. data/spec/extensions/hook_class_methods_spec.rb +0 -429
  583. data/spec/extensions/identifier_columns_spec.rb +0 -17
  584. data/spec/extensions/inflector_spec.rb +0 -183
  585. data/spec/extensions/input_transformer_spec.rb +0 -54
  586. data/spec/extensions/insert_returning_select_spec.rb +0 -46
  587. data/spec/extensions/instance_filters_spec.rb +0 -79
  588. data/spec/extensions/instance_hooks_spec.rb +0 -276
  589. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  590. data/spec/extensions/json_serializer_spec.rb +0 -304
  591. data/spec/extensions/lazy_attributes_spec.rb +0 -170
  592. data/spec/extensions/list_spec.rb +0 -278
  593. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  594. data/spec/extensions/many_through_many_spec.rb +0 -2172
  595. data/spec/extensions/meta_def_spec.rb +0 -21
  596. data/spec/extensions/migration_spec.rb +0 -728
  597. data/spec/extensions/modification_detection_spec.rb +0 -80
  598. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
  599. data/spec/extensions/named_timezones_spec.rb +0 -108
  600. data/spec/extensions/nested_attributes_spec.rb +0 -697
  601. data/spec/extensions/no_auto_literal_strings_spec.rb +0 -65
  602. data/spec/extensions/null_dataset_spec.rb +0 -85
  603. data/spec/extensions/optimistic_locking_spec.rb +0 -128
  604. data/spec/extensions/pagination_spec.rb +0 -118
  605. data/spec/extensions/pg_array_associations_spec.rb +0 -736
  606. data/spec/extensions/pg_array_ops_spec.rb +0 -143
  607. data/spec/extensions/pg_array_spec.rb +0 -390
  608. data/spec/extensions/pg_enum_spec.rb +0 -92
  609. data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
  610. data/spec/extensions/pg_hstore_spec.rb +0 -206
  611. data/spec/extensions/pg_inet_ops_spec.rb +0 -101
  612. data/spec/extensions/pg_inet_spec.rb +0 -52
  613. data/spec/extensions/pg_interval_spec.rb +0 -76
  614. data/spec/extensions/pg_json_ops_spec.rb +0 -275
  615. data/spec/extensions/pg_json_spec.rb +0 -218
  616. data/spec/extensions/pg_loose_count_spec.rb +0 -17
  617. data/spec/extensions/pg_range_ops_spec.rb +0 -58
  618. data/spec/extensions/pg_range_spec.rb +0 -473
  619. data/spec/extensions/pg_row_ops_spec.rb +0 -60
  620. data/spec/extensions/pg_row_plugin_spec.rb +0 -62
  621. data/spec/extensions/pg_row_spec.rb +0 -360
  622. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
  623. data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
  624. data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
  625. data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
  626. data/spec/extensions/prepared_statements_spec.rb +0 -103
  627. data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
  628. data/spec/extensions/pretty_table_spec.rb +0 -92
  629. data/spec/extensions/query_literals_spec.rb +0 -183
  630. data/spec/extensions/query_spec.rb +0 -102
  631. data/spec/extensions/rcte_tree_spec.rb +0 -392
  632. data/spec/extensions/round_timestamps_spec.rb +0 -43
  633. data/spec/extensions/schema_caching_spec.rb +0 -41
  634. data/spec/extensions/schema_dumper_spec.rb +0 -814
  635. data/spec/extensions/schema_spec.rb +0 -117
  636. data/spec/extensions/scissors_spec.rb +0 -26
  637. data/spec/extensions/select_remove_spec.rb +0 -38
  638. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
  639. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  640. data/spec/extensions/serialization_spec.rb +0 -362
  641. data/spec/extensions/server_block_spec.rb +0 -90
  642. data/spec/extensions/server_logging_spec.rb +0 -45
  643. data/spec/extensions/set_overrides_spec.rb +0 -61
  644. data/spec/extensions/sharding_spec.rb +0 -198
  645. data/spec/extensions/shared_caching_spec.rb +0 -175
  646. data/spec/extensions/single_table_inheritance_spec.rb +0 -297
  647. data/spec/extensions/singular_table_names_spec.rb +0 -22
  648. data/spec/extensions/skip_create_refresh_spec.rb +0 -17
  649. data/spec/extensions/spec_helper.rb +0 -71
  650. data/spec/extensions/split_array_nil_spec.rb +0 -24
  651. data/spec/extensions/split_values_spec.rb +0 -22
  652. data/spec/extensions/sql_comments_spec.rb +0 -27
  653. data/spec/extensions/sql_expr_spec.rb +0 -60
  654. data/spec/extensions/static_cache_spec.rb +0 -361
  655. data/spec/extensions/string_agg_spec.rb +0 -85
  656. data/spec/extensions/string_date_time_spec.rb +0 -95
  657. data/spec/extensions/string_stripper_spec.rb +0 -68
  658. data/spec/extensions/subclasses_spec.rb +0 -66
  659. data/spec/extensions/subset_conditions_spec.rb +0 -38
  660. data/spec/extensions/table_select_spec.rb +0 -71
  661. data/spec/extensions/tactical_eager_loading_spec.rb +0 -136
  662. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  663. data/spec/extensions/timestamps_spec.rb +0 -175
  664. data/spec/extensions/to_dot_spec.rb +0 -154
  665. data/spec/extensions/touch_spec.rb +0 -203
  666. data/spec/extensions/tree_spec.rb +0 -274
  667. data/spec/extensions/typecast_on_load_spec.rb +0 -80
  668. data/spec/extensions/unlimited_update_spec.rb +0 -20
  669. data/spec/extensions/update_or_create_spec.rb +0 -87
  670. data/spec/extensions/update_primary_key_spec.rb +0 -100
  671. data/spec/extensions/update_refresh_spec.rb +0 -53
  672. data/spec/extensions/uuid_spec.rb +0 -106
  673. data/spec/extensions/validate_associated_spec.rb +0 -52
  674. data/spec/extensions/validation_class_methods_spec.rb +0 -1027
  675. data/spec/extensions/validation_helpers_spec.rb +0 -554
  676. data/spec/extensions/xml_serializer_spec.rb +0 -207
  677. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  678. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  679. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  680. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  681. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  682. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  683. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  684. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  685. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  686. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  687. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  688. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  689. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  690. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  691. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  692. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  693. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  694. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  695. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  696. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  697. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  698. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  699. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  700. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  701. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  702. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  703. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  704. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  705. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  706. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  707. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  708. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  709. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  710. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  711. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  712. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  713. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  714. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  715. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  716. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  717. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  718. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  719. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  720. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  721. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  722. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  723. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  724. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  725. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  726. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  727. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  728. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  729. data/spec/guards_helper.rb +0 -55
  730. data/spec/integration/associations_test.rb +0 -2506
  731. data/spec/integration/database_test.rb +0 -113
  732. data/spec/integration/dataset_test.rb +0 -1858
  733. data/spec/integration/eager_loader_test.rb +0 -687
  734. data/spec/integration/migrator_test.rb +0 -262
  735. data/spec/integration/model_test.rb +0 -230
  736. data/spec/integration/plugin_test.rb +0 -2297
  737. data/spec/integration/prepared_statement_test.rb +0 -467
  738. data/spec/integration/schema_test.rb +0 -815
  739. data/spec/integration/spec_helper.rb +0 -56
  740. data/spec/integration/timezone_test.rb +0 -86
  741. data/spec/integration/transaction_test.rb +0 -406
  742. data/spec/integration/type_test.rb +0 -133
  743. data/spec/model/association_reflection_spec.rb +0 -565
  744. data/spec/model/associations_spec.rb +0 -4589
  745. data/spec/model/base_spec.rb +0 -759
  746. data/spec/model/class_dataset_methods_spec.rb +0 -150
  747. data/spec/model/dataset_methods_spec.rb +0 -149
  748. data/spec/model/eager_loading_spec.rb +0 -2197
  749. data/spec/model/hooks_spec.rb +0 -604
  750. data/spec/model/inflector_spec.rb +0 -26
  751. data/spec/model/model_spec.rb +0 -1097
  752. data/spec/model/plugins_spec.rb +0 -299
  753. data/spec/model/record_spec.rb +0 -2162
  754. data/spec/model/spec_helper.rb +0 -46
  755. data/spec/model/validations_spec.rb +0 -193
  756. data/spec/model_no_assoc_spec.rb +0 -1
  757. data/spec/model_spec.rb +0 -1
  758. data/spec/plugin_spec.rb +0 -1
  759. data/spec/sequel_coverage.rb +0 -15
  760. data/spec/spec_config.rb +0 -10
@@ -1,63 +1,113 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel.require 'adapters/utils/pg_types'
3
+ require_relative '../utils/unmodified_identifiers'
4
4
 
5
5
  module Sequel
6
6
  # Top level module for holding all PostgreSQL-related modules and classes
7
- # for Sequel. There are a few module level accessors that are added via
8
- # metaprogramming. These are:
9
- #
10
- # client_min_messages :: Change the minimum level of messages that PostgreSQL will send to the
11
- # the client. The PostgreSQL default is NOTICE, the Sequel default is
12
- # WARNING. Set to nil to not change the server default. Overridable on
13
- # a per instance basis via the :client_min_messages option.
14
- # force_standard_strings :: Set to false to not force the use of standard strings. Overridable
15
- # on a per instance basis via the :force_standard_strings option.
16
- #
17
- # It is not recommened you use these module-level accessors. Instead,
18
- # use the database option to make the setting per-Database.
19
- #
20
- # All adapters that connect to PostgreSQL support the following option in
21
- # addition to those mentioned above:
7
+ # for Sequel. All adapters that connect to PostgreSQL support the following options:
22
8
  #
9
+ # :client_min_messages :: Change the minimum level of messages that PostgreSQL will send to the
10
+ # the client. The PostgreSQL default is NOTICE, the Sequel default is
11
+ # WARNING. Set to nil to not change the server default. Overridable on
12
+ # a per instance basis via the :client_min_messages option.
13
+ # :force_standard_strings :: Set to false to not force the use of standard strings. Overridable
14
+ # on a per instance basis via the :force_standard_strings option.
23
15
  # :search_path :: Set the schema search_path for this Database's connections.
24
16
  # Allows to to set which schemas do not need explicit
25
17
  # qualification, and in which order to check the schemas when
26
18
  # an unqualified object is referenced.
27
19
  module Postgres
28
- # Array of exceptions that need to be converted. JDBC
29
- # uses NativeExceptions, the native adapter uses PGError.
30
- CONVERTED_EXCEPTIONS = []
31
-
32
- @client_min_messages = :warning
33
- @force_standard_strings = true
34
-
35
- class << self
36
- # By default, Sequel sets the minimum level of log messages sent to the client
37
- # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
38
- # of mostly useless messages when running migrations, such as a couple of lines
39
- # for every serial primary key field.
40
- attr_accessor :client_min_messages
41
-
42
- # By default, Sequel forces the use of standard strings, so that
43
- # '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
44
- # to interpreting plain strings, newer versions use standard strings by
45
- # default. Sequel assumes that SQL standard strings will be used. Setting
46
- # this to false means Sequel will use the database's default.
47
- attr_accessor :force_standard_strings
20
+ Sequel::Database.set_shared_adapter_scheme(:postgres, self)
21
+
22
+ NAN = 0.0/0.0
23
+ PLUS_INFINITY = 1.0/0.0
24
+ MINUS_INFINITY = -1.0/0.0
25
+
26
+ boolean = Object.new
27
+ def boolean.call(s) s == 't' end
28
+ integer = Object.new
29
+ def integer.call(s) s.to_i end
30
+ float = Object.new
31
+ def float.call(s)
32
+ case s
33
+ when 'NaN'
34
+ NAN
35
+ when 'Infinity'
36
+ PLUS_INFINITY
37
+ when '-Infinity'
38
+ MINUS_INFINITY
39
+ else
40
+ s.to_f
41
+ end
42
+ end
43
+ date = Object.new
44
+ def date.call(s) ::Date.new(*s.split('-').map(&:to_i)) end
45
+ TYPE_TRANSLATOR_DATE = date.freeze
46
+ bytea = Object.new
47
+ def bytea.call(str)
48
+ str = if str =~ /\A\\x/
49
+ # PostgreSQL 9.0+ bytea hex format
50
+ str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
51
+ else
52
+ # Historical PostgreSQL bytea escape format
53
+ str.gsub(/\\(\\|'|[0-3][0-7][0-7])/) {|s|
54
+ if s.size == 2 then s[1,1] else s[1,3].oct.chr end
55
+ }
56
+ end
57
+ ::Sequel::SQL::Blob.new(str)
58
+ end
59
+
60
+ CONVERSION_PROCS = {}
61
+
62
+ {
63
+ [16] => boolean,
64
+ [17] => bytea,
65
+ [20, 21, 23, 26] => integer,
66
+ [700, 701] => float,
67
+ [1700] => ::Kernel.method(:BigDecimal),
68
+ [1083, 1266] => ::Sequel.method(:string_to_time),
69
+ [1082] => ::Sequel.method(:string_to_date),
70
+ [1184, 1114] => ::Sequel.method(:database_to_application_timestamp),
71
+ }.each do |k,v|
72
+ k.each do |n|
73
+ CONVERSION_PROCS[n] = v
74
+ end
75
+ end
76
+ CONVERSION_PROCS.freeze
77
+
78
+ module MockAdapterDatabaseMethods
79
+ def bound_variable_arg(arg, conn)
80
+ arg
81
+ end
82
+
83
+ def primary_key(table)
84
+ :id
85
+ end
48
86
  end
49
87
 
50
- class CreateTableGenerator < Sequel::Schema::Generator
88
+ def self.mock_adapter_setup(db)
89
+ db.instance_exec do
90
+ @server_version = 140000
91
+ initialize_postgres_adapter
92
+ extend(MockAdapterDatabaseMethods)
93
+ end
94
+ end
95
+
96
+ class CreateTableGenerator < Sequel::Schema::CreateTableGenerator
51
97
  # Add an exclusion constraint when creating the table. Elements should be
52
98
  # an array of 2 element arrays, with the first element being the column or
53
99
  # expression the exclusion constraint is applied to, and the second element
54
- # being the operator to use for the column/expression to check for exclusion.
55
- #
56
- # Example:
100
+ # being the operator to use for the column/expression to check for exclusion:
57
101
  #
58
102
  # exclude([[:col1, '&&'], [:col2, '=']])
59
103
  # # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
60
104
  #
105
+ # To use a custom operator class, you need to use Sequel.lit with the expression
106
+ # and operator class:
107
+ #
108
+ # exclude([[Sequel.lit('col1 inet_ops'), '&&'], [:col2, '=']])
109
+ # # EXCLUDE USING gist (col1 inet_ops WITH &&, col2 WITH =)
110
+ #
61
111
  # Options supported:
62
112
  #
63
113
  # :name :: Name the constraint with the given name (useful if you may
@@ -84,21 +134,105 @@ module Sequel
84
134
  end
85
135
  end
86
136
 
137
+ # Generator used for creating tables that are partitions of other tables.
138
+ class CreatePartitionOfTableGenerator
139
+ MINVALUE = Sequel.lit('MINVALUE').freeze
140
+ MAXVALUE = Sequel.lit('MAXVALUE').freeze
141
+
142
+ def initialize(&block)
143
+ instance_exec(&block)
144
+ end
145
+
146
+ # The minimum value of the data type used in range partitions, useful
147
+ # as an argument to #from.
148
+ def minvalue
149
+ MINVALUE
150
+ end
151
+
152
+ # The minimum value of the data type used in range partitions, useful
153
+ # as an argument to #to.
154
+ def maxvalue
155
+ MAXVALUE
156
+ end
157
+
158
+ # Assumes range partitioning, sets the inclusive minimum value of the range for
159
+ # this partition.
160
+ def from(*v)
161
+ @from = v
162
+ end
163
+
164
+ # Assumes range partitioning, sets the exclusive maximum value of the range for
165
+ # this partition.
166
+ def to(*v)
167
+ @to = v
168
+ end
169
+
170
+ # Assumes list partitioning, sets the values to be included in this partition.
171
+ def values_in(*v)
172
+ @in = v
173
+ end
174
+
175
+ # Assumes hash partitioning, sets the modulus for this parition.
176
+ def modulus(v)
177
+ @modulus = v
178
+ end
179
+
180
+ # Assumes hash partitioning, sets the remainder for this parition.
181
+ def remainder(v)
182
+ @remainder = v
183
+ end
184
+
185
+ # Sets that this is a default partition, where values not in other partitions
186
+ # are stored.
187
+ def default
188
+ @default = true
189
+ end
190
+
191
+ # The from and to values of this partition for a range partition.
192
+ def range
193
+ [@from, @to]
194
+ end
195
+
196
+ # The values to include in this partition for a list partition.
197
+ def list
198
+ @in
199
+ end
200
+
201
+ # The modulus and remainder to use for this partition for a hash partition.
202
+ def hash_values
203
+ [@modulus, @remainder]
204
+ end
205
+
206
+ # Determine the appropriate partition type for this partition by which methods
207
+ # were called on it.
208
+ def partition_type
209
+ raise Error, "Unable to determine partition type, multiple different partitioning methods called" if [@from || @to, @list, @modulus || @remainder, @default].compact.length > 1
210
+
211
+ if @from || @to
212
+ raise Error, "must call both from and to when creating a partition of a table if calling either" unless @from && @to
213
+ :range
214
+ elsif @in
215
+ :list
216
+ elsif @modulus || @remainder
217
+ raise Error, "must call both modulus and remainder when creating a partition of a table if calling either" unless @modulus && @remainder
218
+ :hash
219
+ elsif @default
220
+ :default
221
+ else
222
+ raise Error, "unable to determine partition type, no partitioning methods called"
223
+ end
224
+ end
225
+ end
226
+
87
227
  # Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
88
228
  class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
89
229
 
90
- # Methods shared by Database instances that connect to PostgreSQL.
91
230
  module DatabaseMethods
92
- extend Sequel::Database::ResetIdentifierMangling
231
+ include UnmodifiedIdentifiers::DatabaseMethods
93
232
 
94
- PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
95
- RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
96
- FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'.freeze=>:no_action, 'r'.freeze=>:restrict, 'c'.freeze=>:cascade, 'n'.freeze=>:set_null, 'd'.freeze=>:set_default}.freeze
97
- POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
98
- UNLOGGED = 'UNLOGGED '.freeze
99
- ON_COMMIT = {
100
- :drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS',
101
- }.freeze
233
+ FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'=>:no_action, 'r'=>:restrict, 'c'=>:cascade, 'n'=>:set_null, 'd'=>:set_default}.freeze
234
+ ON_COMMIT = {:drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS'}.freeze
235
+ ON_COMMIT.each_value(&:freeze)
102
236
 
103
237
  # SQL fragment for custom sequences (ones not created by serial primary key),
104
238
  # Returning the schema and literal form of the sequence name, by parsing
@@ -106,10 +240,10 @@ module Sequel
106
240
  SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
107
241
  SELECT name.nspname AS "schema",
108
242
  CASE
109
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
110
- substr(split_part(def.adsrc, '''', 2),
111
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
112
- ELSE split_part(def.adsrc, '''', 2)
243
+ WHEN split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2) ~ '.' THEN
244
+ substr(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2),
245
+ strpos(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2), '.')+1)
246
+ ELSE split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2)
113
247
  END AS "sequence"
114
248
  FROM pg_class t
115
249
  JOIN pg_namespace name ON (t.relnamespace = name.oid)
@@ -117,7 +251,7 @@ module Sequel
117
251
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
118
252
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
119
253
  WHERE cons.contype = 'p'
120
- AND def.adsrc ~* 'nextval'
254
+ AND pg_get_expr(def.adbin, attr.attrelid) ~* 'nextval'
121
255
  end_sql
122
256
  ).strip.gsub(/\s+/, ' ').freeze
123
257
 
@@ -156,21 +290,121 @@ module Sequel
156
290
  # having callable values for the conversion proc for that type.
157
291
  attr_reader :conversion_procs
158
292
 
159
- # Add a conversion proc for a named type. This should be used
160
- # for types without fixed OIDs, which includes all types that
161
- # are not included in a default PostgreSQL installation. If
162
- # a block is given, it is used as the conversion proc, otherwise
163
- # the conversion proc is looked up in the PG_NAMED_TYPES hash.
293
+ # Set a conversion proc for the given oid. The callable can
294
+ # be passed either as a argument or a block.
295
+ def add_conversion_proc(oid, callable=nil, &block)
296
+ conversion_procs[oid] = callable || block
297
+ end
298
+
299
+ # Add a conversion proc for a named type, using the given block.
300
+ # This should be used for types without fixed OIDs, which includes all types that
301
+ # are not included in a default PostgreSQL installation.
164
302
  def add_named_conversion_proc(name, &block)
165
- add_named_conversion_procs(conversion_procs, name=>(block || PG_NAMED_TYPES[name]))
303
+ unless oid = from(:pg_type).where(:typtype=>['b', 'e'], :typname=>name.to_s).get(:oid)
304
+ raise Error, "No matching type in pg_type for #{name.inspect}"
305
+ end
306
+ add_conversion_proc(oid, block)
166
307
  end
167
308
 
168
- # Commit an existing prepared transaction with the given transaction
169
- # identifier string.
170
309
  def commit_prepared_transaction(transaction_id, opts=OPTS)
171
310
  run("COMMIT PREPARED #{literal(transaction_id)}", opts)
172
311
  end
173
312
 
313
+ # A hash of metadata for CHECK constraints on the table.
314
+ # Keys are CHECK constraint name symbols. Values are hashes with the following keys:
315
+ # :definition :: An SQL fragment for the definition of the constraint
316
+ # :columns :: An array of column symbols for the columns referenced in the constraint,
317
+ # can be an empty array if the database cannot deteremine the column symbols.
318
+ def check_constraints(table)
319
+ m = output_identifier_meth
320
+
321
+ rows = metadata_dataset.
322
+ from{pg_constraint.as(:co)}.
323
+ left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
324
+ where(:conrelid=>regclass_oid(table), :contype=>'c').
325
+ select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
326
+
327
+ hash = {}
328
+ rows.each do |row|
329
+ constraint = m.call(row[:constraint])
330
+ entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
331
+ entry[:columns] << m.call(row[:column]) if row[:column]
332
+ end
333
+
334
+ hash
335
+ end
336
+
337
+ # Convert the first primary key column in the +table+ from being a serial column to being an identity column.
338
+ # If the column is already an identity column, assume it was already converted and make no changes.
339
+ #
340
+ # Only supported on PostgreSQL 10.2+, since on those versions Sequel will use identity columns
341
+ # instead of serial columns for auto incrementing primary keys. Only supported when running as
342
+ # a superuser, since regular users cannot modify system tables, and there is no way to keep an
343
+ # existing sequence when changing an existing column to be an identity column.
344
+ #
345
+ # This method can raise an exception in at least the following cases where it may otherwise succeed
346
+ # (there may be additional cases not listed here):
347
+ #
348
+ # * The serial column was added after table creation using PostgreSQL <7.3
349
+ # * A regular index also exists on the column (such an index can probably be dropped as the
350
+ # primary key index should suffice)
351
+ #
352
+ # Options:
353
+ # :column :: Specify the column to convert instead of using the first primary key column
354
+ # :server :: Run the SQL on the given server
355
+ def convert_serial_to_identity(table, opts=OPTS)
356
+ raise Error, "convert_serial_to_identity is only supported on PostgreSQL 10.2+" unless server_version >= 100002
357
+
358
+ server = opts[:server]
359
+ server_hash = server ? {:server=>server} : OPTS
360
+ ds = dataset
361
+ ds = ds.server(server) if server
362
+
363
+ raise Error, "convert_serial_to_identity requires superuser permissions" unless ds.get{current_setting('is_superuser')} == 'on'
364
+
365
+ table_oid = regclass_oid(table)
366
+ im = input_identifier_meth
367
+ unless column = (opts[:column] || ((sch = schema(table).find{|_, sc| sc[:primary_key] && sc[:auto_increment]}) && sch[0]))
368
+ raise Error, "could not determine column to convert from serial to identity automatically"
369
+ end
370
+ column = im.call(column)
371
+
372
+ column_num = ds.from(:pg_attribute).
373
+ where(:attrelid=>table_oid, :attname=>column).
374
+ get(:attnum)
375
+
376
+ pg_class = Sequel.cast('pg_class', :regclass)
377
+ res = ds.from(:pg_depend).
378
+ where(:refclassid=>pg_class, :refobjid=>table_oid, :refobjsubid=>column_num, :classid=>pg_class, :objsubid=>0, :deptype=>%w'a i').
379
+ select_map([:objid, Sequel.as({:deptype=>'i'}, :v)])
380
+
381
+ case res.length
382
+ when 0
383
+ raise Error, "unable to find related sequence when converting serial to identity"
384
+ when 1
385
+ seq_oid, already_identity = res.first
386
+ else
387
+ raise Error, "more than one linked sequence found when converting serial to identity"
388
+ end
389
+
390
+ return if already_identity
391
+
392
+ transaction(server_hash) do
393
+ run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
394
+
395
+ ds.from(:pg_depend).
396
+ where(:classid=>pg_class, :objid=>seq_oid, :objsubid=>0, :deptype=>'a').
397
+ update(:deptype=>'i')
398
+
399
+ ds.from(:pg_attribute).
400
+ where(:attrelid=>table_oid, :attname=>column).
401
+ update(:attidentity=>'d')
402
+ end
403
+
404
+ remove_cached_schema(table)
405
+ nil
406
+ end
407
+
174
408
  # Creates the function in the database. Arguments:
175
409
  # name :: name of the function to create
176
410
  # definition :: string definition of the function, or object file for a dynamically loaded C function.
@@ -180,6 +414,7 @@ module Sequel
180
414
  # 2 :: argument name
181
415
  # 3 :: argument mode (e.g. in, out, inout)
182
416
  # :behavior :: Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
417
+ # :parallel :: The thread safety attribute of the function. Should be SAFE, UNSAFE, RESTRICTED. PostgreSQL assumes UNSAFE by default.
183
418
  # :cost :: The estimated cost of the function, used by the query planner.
184
419
  # :language :: The language the function uses. SQL is the default.
185
420
  # :link_symbol :: For a dynamically loaded see function, the function's link symbol if different from the definition argument.
@@ -215,6 +450,26 @@ module Sequel
215
450
  self << create_schema_sql(name, opts)
216
451
  end
217
452
 
453
+ # Support partitions of tables using the :partition_of option.
454
+ def create_table(name, options=OPTS, &block)
455
+ if options[:partition_of]
456
+ create_partition_of_table_from_generator(name, CreatePartitionOfTableGenerator.new(&block), options)
457
+ return
458
+ end
459
+
460
+ super
461
+ end
462
+
463
+ # Support partitions of tables using the :partition_of option.
464
+ def create_table?(name, options=OPTS, &block)
465
+ if options[:partition_of]
466
+ create_table(name, options.merge!(:if_not_exists=>true), &block)
467
+ return
468
+ end
469
+
470
+ super
471
+ end
472
+
218
473
  # Create a trigger in the database. Arguments:
219
474
  # table :: the table on which this trigger operates
220
475
  # name :: the name of this trigger
@@ -225,12 +480,12 @@ module Sequel
225
480
  # :each_row :: Calls the trigger for each row instead of for each statement.
226
481
  # :events :: Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
227
482
  # the trigger is called for insert, update, or delete.
483
+ # :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
228
484
  # :when :: A filter to use for the trigger
229
485
  def create_trigger(table, name, function, opts=OPTS)
230
486
  self << create_trigger_sql(table, name, function, opts)
231
487
  end
232
488
 
233
- # PostgreSQL uses the :postgres database type.
234
489
  def database_type
235
490
  :postgres
236
491
  end
@@ -285,75 +540,150 @@ module Sequel
285
540
 
286
541
  # Return full foreign key information using the pg system tables, including
287
542
  # :name, :on_delete, :on_update, and :deferrable entries in the hashes.
543
+ #
544
+ # Supports additional options:
545
+ # :reverse :: Instead of returning foreign keys in the current table, return
546
+ # foreign keys in other tables that reference the current table.
547
+ # :schema :: Set to true to have the :table value in the hashes be a qualified
548
+ # identifier. Set to false to use a separate :schema value with
549
+ # the related schema. Defaults to whether the given table argument
550
+ # is a qualified identifier.
288
551
  def foreign_key_list(table, opts=OPTS)
289
552
  m = output_identifier_meth
290
553
  schema, _ = opts.fetch(:schema, schema_and_table(table))
291
- range = 0...32
292
-
293
- base_ds = metadata_dataset.
294
- from(:pg_constraint___co).
295
- join(:pg_class___cl, :oid=>:conrelid).
296
- where(:cl__relkind=>'r', :co__contype=>'f', :cl__oid=>regclass_oid(table))
297
-
298
- # We split the parsing into two separate queries, which are merged manually later.
299
- # This is because PostgreSQL stores both the referencing and referenced columns in
300
- # arrays, and I don't know a simple way to not create a cross product, as PostgreSQL
301
- # doesn't appear to have a function that takes an array and element and gives you
302
- # the index of that element in the array.
303
-
304
- ds = base_ds.
305
- join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
306
- order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
307
- select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
308
- SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
309
-
310
- ref_ds = base_ds.
311
- join(:pg_class___cl2, :oid=>:co__confrelid).
312
- join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
313
- order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__confkey, [x]), x]}, 32, :att2__attnum)).
314
- select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
315
-
316
- # If a schema is given, we only search in that schema, and the returned :table
317
- # entry is schema qualified as well.
318
- if schema
319
- ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
320
- select_more(:nsp2__nspname___schema)
554
+ oid = regclass_oid(table)
555
+ reverse = opts[:reverse]
556
+
557
+ if reverse
558
+ ctable = Sequel[:att2]
559
+ cclass = Sequel[:cl2]
560
+ rtable = Sequel[:att]
561
+ rclass = Sequel[:cl]
562
+ else
563
+ ctable = Sequel[:att]
564
+ cclass = Sequel[:cl]
565
+ rtable = Sequel[:att2]
566
+ rclass = Sequel[:cl2]
567
+ end
568
+
569
+ if server_version >= 90500
570
+ cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
571
+ rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
572
+ # :nocov:
573
+ else
574
+ range = 0...32
575
+ cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
576
+ rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
577
+ # :nocov:
578
+ end
579
+
580
+ ds = metadata_dataset.
581
+ from{pg_constraint.as(:co)}.
582
+ join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
583
+ join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
584
+ join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
585
+ join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
586
+ join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
587
+ order{[co[:conname], cpos]}.
588
+ where{{
589
+ cl[:relkind]=>%w'r p',
590
+ co[:contype]=>'f',
591
+ cl[:oid]=>oid,
592
+ cpos=>rpos
593
+ }}.
594
+ select{[
595
+ co[:conname].as(:name),
596
+ ctable[:attname].as(:column),
597
+ co[:confupdtype].as(:on_update),
598
+ co[:confdeltype].as(:on_delete),
599
+ cl2[:relname].as(:table),
600
+ rtable[:attname].as(:refcolumn),
601
+ SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
602
+ nsp[:nspname].as(:schema)
603
+ ]}
604
+
605
+ if reverse
606
+ ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
321
607
  end
322
608
 
323
609
  h = {}
324
610
  fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
611
+
325
612
  ds.each do |row|
326
- if r = h[row[:name]]
613
+ if reverse
614
+ key = [row[:schema], row[:table], row[:name]]
615
+ else
616
+ key = row[:name]
617
+ end
618
+
619
+ if r = h[key]
327
620
  r[:columns] << m.call(row[:column])
621
+ r[:key] << m.call(row[:refcolumn])
328
622
  else
329
- h[row[:name]] = {:name=>m.call(row[:name]), :columns=>[m.call(row[:column])], :on_update=>fklod_map[row[:on_update]], :on_delete=>fklod_map[row[:on_delete]], :deferrable=>row[:deferrable]}
623
+ entry = h[key] = {
624
+ :name=>m.call(row[:name]),
625
+ :columns=>[m.call(row[:column])],
626
+ :key=>[m.call(row[:refcolumn])],
627
+ :on_update=>fklod_map[row[:on_update]],
628
+ :on_delete=>fklod_map[row[:on_delete]],
629
+ :deferrable=>row[:deferrable],
630
+ :table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
631
+ }
632
+
633
+ unless schema
634
+ # If not combining schema information into the :table entry
635
+ # include it as a separate entry.
636
+ entry[:schema] = m.call(row[:schema])
637
+ end
330
638
  end
331
639
  end
332
- ref_ds.each do |row|
333
- r = h[row[:name]]
334
- r[:table] ||= schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
335
- r[:key] ||= []
336
- r[:key] << m.call(row[:refcolumn])
337
- end
640
+
338
641
  h.values
339
642
  end
340
643
 
644
+ def freeze
645
+ server_version
646
+ supports_prepared_transactions?
647
+ @conversion_procs.freeze
648
+ super
649
+ end
650
+
341
651
  # Use the pg_* system tables to determine indexes on a table
342
652
  def indexes(table, opts=OPTS)
343
653
  m = output_identifier_meth
344
- range = 0...32
345
- attnums = server_version >= 80100 ? SQL::Function.new(:ANY, :ind__indkey) : range.map{|x| SQL::Subscript.new(:ind__indkey, [x])}
346
- ds = metadata_dataset.
347
- from(:pg_class___tab).
348
- join(:pg_index___ind, :indrelid=>:oid).
349
- join(:pg_class___indc, :oid=>:indexrelid).
350
- join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
351
- left_join(:pg_constraint___con, :conname=>:indc__relname).
352
- filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true, :tab__oid=>regclass_oid(table, opts)).
353
- order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
354
- select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column, :con__condeferrable___deferrable)
654
+ oid = regclass_oid(table, opts)
655
+
656
+ if server_version >= 90500
657
+ order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
658
+ # :nocov:
659
+ else
660
+ range = 0...32
661
+ order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
662
+ # :nocov:
663
+ end
355
664
 
356
- ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
665
+ attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
666
+
667
+ ds = metadata_dataset.
668
+ from{pg_class.as(:tab)}.
669
+ join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
670
+ join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
671
+ join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
672
+ left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
673
+ where{{
674
+ indc[:relkind]=>'i',
675
+ ind[:indisprimary]=>false,
676
+ :indexprs=>nil,
677
+ :indisvalid=>true,
678
+ tab[:oid]=>oid}}.
679
+ order(*order).
680
+ select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
681
+
682
+ ds = ds.where(:indpred=>nil) unless opts[:include_partial]
683
+ # :nocov:
684
+ ds = ds.where(:indisready=>true) if server_version >= 80300
685
+ ds = ds.where(:indislive=>true) if server_version >= 90300
686
+ # :nocov:
357
687
 
358
688
  indexes = {}
359
689
  ds.each do |r|
@@ -365,7 +695,7 @@ module Sequel
365
695
 
366
696
  # Dataset containing all current database locks
367
697
  def locks
368
- dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
698
+ dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select{[pg_class[:relname], Sequel::SQL::ColumnAll.new(:pg_locks)]}
369
699
  end
370
700
 
371
701
  # Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
@@ -421,28 +751,30 @@ module Sequel
421
751
  run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
422
752
  end
423
753
 
424
- # Reset the database's conversion procs, requires a server query if there
425
- # any named types.
426
- def reset_conversion_procs
427
- @conversion_procs = get_conversion_procs
428
- conversion_procs_updated
429
- @conversion_procs
430
- end
431
-
432
754
  # Reset the primary key sequence for the given table, basing it on the
433
755
  # maximum current value of the table's primary key.
434
756
  def reset_primary_key_sequence(table)
435
757
  return unless seq = primary_key_sequence(table)
436
758
  pk = SQL::Identifier.new(primary_key(table))
437
759
  db = self
438
- seq_ds = db.from(LiteralString.new(seq))
439
760
  s, t = schema_and_table(table)
440
761
  table = Sequel.qualify(s, t) if s
441
- get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
762
+
763
+ if server_version >= 100000
764
+ seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
765
+ increment_by = :seqincrement
766
+ min_value = :seqmin
767
+ # :nocov:
768
+ else
769
+ seq_ds = metadata_dataset.from(LiteralString.new(seq))
770
+ increment_by = :increment_by
771
+ min_value = :min_value
772
+ # :nocov:
773
+ end
774
+
775
+ get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
442
776
  end
443
777
 
444
- # Rollback an existing prepared transaction with the given transaction
445
- # identifier string.
446
778
  def rollback_prepared_transaction(transaction_id, opts=OPTS)
447
779
  run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
448
780
  end
@@ -450,24 +782,18 @@ module Sequel
450
782
  # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
451
783
  # managing incrementing primary keys.
452
784
  def serial_primary_key_options
453
- {:primary_key => true, :serial => true, :type=>Integer}
785
+ # :nocov:
786
+ auto_increment_key = server_version >= 100002 ? :identity : :serial
787
+ # :nocov:
788
+ {:primary_key => true, auto_increment_key => true, :type=>Integer}
454
789
  end
455
790
 
456
791
  # The version of the PostgreSQL server, used for determining capability.
457
792
  def server_version(server=nil)
458
793
  return @server_version if @server_version
459
- @server_version = synchronize(server) do |conn|
460
- (conn.server_version rescue nil) if conn.respond_to?(:server_version)
461
- end
462
- unless @server_version
463
- @server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
464
- (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
465
- else
466
- 0
467
- end
468
- end
469
- warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
470
- @server_version
794
+ ds = dataset
795
+ ds = ds.server(server) if server
796
+ @server_version = swallow_database_error{ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value} || 0
471
797
  end
472
798
 
473
799
  # PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
@@ -532,23 +858,24 @@ module Sequel
532
858
  # :schema :: The schema to search
533
859
  # :server :: The server to use
534
860
  def tables(opts=OPTS, &block)
535
- pg_class_relname('r', opts, &block)
861
+ pg_class_relname(['r', 'p'], opts, &block)
536
862
  end
537
863
 
538
864
  # Check whether the given type name string/symbol (e.g. :hstore) is supported by
539
865
  # the database.
540
866
  def type_supported?(type)
541
- @supported_types ||= {}
542
- @supported_types.fetch(type){@supported_types[type] = (from(:pg_type).filter(:typtype=>'b', :typname=>type.to_s).count > 0)}
867
+ Sequel.synchronize{return @supported_types[type] if @supported_types.has_key?(type)}
868
+ supported = from(:pg_type).where(:typtype=>'b', :typname=>type.to_s).count > 0
869
+ Sequel.synchronize{return @supported_types[type] = supported}
543
870
  end
544
871
 
545
872
  # Creates a dataset that uses the VALUES clause:
546
873
  #
547
874
  # DB.values([[1, 2], [3, 4]])
548
- # VALUES ((1, 2), (3, 4))
875
+ # # VALUES ((1, 2), (3, 4))
549
876
  #
550
877
  # DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
551
- # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
878
+ # # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
552
879
  def values(v)
553
880
  @default_dataset.clone(:values=>v)
554
881
  end
@@ -556,28 +883,22 @@ module Sequel
556
883
  # Array of symbols specifying view names in the current database.
557
884
  #
558
885
  # Options:
886
+ # :materialized :: Return materialized views
559
887
  # :qualify :: Return the views as Sequel::SQL::QualifiedIdentifier instances,
560
888
  # using the schema the view is located in as the qualifier.
561
889
  # :schema :: The schema to search
562
890
  # :server :: The server to use
563
891
  def views(opts=OPTS)
564
- pg_class_relname('v', opts)
892
+ relkind = opts[:materialized] ? 'm' : 'v'
893
+ pg_class_relname(relkind, opts)
565
894
  end
566
895
 
567
896
  private
568
897
 
569
- # Do a type name-to-oid lookup using the database and update the procs
570
- # with the related proc if the database supports the type.
571
- def add_named_conversion_procs(procs, named_procs)
572
- unless (named_procs).empty?
573
- convert_named_procs_to_procs(named_procs).each do |oid, pr|
574
- procs[oid] ||= pr
575
- end
576
- conversion_procs_updated
577
- end
898
+ def alter_table_add_column_sql(table, op)
899
+ "ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
578
900
  end
579
901
 
580
- # Use a PostgreSQL-specific alter table generator
581
902
  def alter_table_generator_class
582
903
  Postgres::AlterTableGenerator
583
904
  end
@@ -637,9 +958,24 @@ module Sequel
637
958
  end
638
959
  end
639
960
 
961
+ # Support identity columns, but only use the identity SQL syntax if no
962
+ # default value is given.
963
+ def column_definition_default_sql(sql, column)
964
+ super
965
+ if !column[:serial] && !['smallserial', 'serial', 'bigserial'].include?(column[:type].to_s) && !column[:default]
966
+ if (identity = column[:identity])
967
+ sql << " GENERATED "
968
+ sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
969
+ sql << " AS IDENTITY"
970
+ elsif (generated = column[:generated_always_as])
971
+ sql << " GENERATED ALWAYS AS (#{literal(generated)}) STORED"
972
+ end
973
+ end
974
+ end
975
+
640
976
  # Handle PostgreSQL specific default format.
641
977
  def column_schema_normalize_default(default, type)
642
- if m = POSTGRES_DEFAULT_RE.match(default)
978
+ if m = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/.match(default)
643
979
  default = m[1] || m[2]
644
980
  end
645
981
  super(default, type)
@@ -661,14 +997,15 @@ module Sequel
661
997
  (super || op[:op] == :validate_constraint) && op[:op] != :rename_column
662
998
  end
663
999
 
664
- VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze
1000
+ VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
665
1001
  # The SQL queries to execute when starting a new connection.
666
- def connection_configuration_sqls
1002
+ def connection_configuration_sqls(opts=@opts)
667
1003
  sqls = []
668
1004
 
669
- sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(@opts.fetch(:force_standard_strings, Postgres.force_standard_strings))
1005
+ sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(opts.fetch(:force_standard_strings, true))
670
1006
 
671
- if (cmm = @opts.fetch(:client_min_messages, Postgres.client_min_messages)) && !cmm.to_s.empty?
1007
+ cmm = opts.fetch(:client_min_messages, :warning)
1008
+ if cmm && !cmm.to_s.empty?
672
1009
  cmm = cmm.to_s.upcase.strip
673
1010
  unless VALID_CLIENT_MIN_MESSAGES.include?(cmm)
674
1011
  raise Error, "Unsupported client_min_messages setting: #{cmm}"
@@ -676,7 +1013,7 @@ module Sequel
676
1013
  sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
677
1014
  end
678
1015
 
679
- if search_path = @opts[:search_path]
1016
+ if search_path = opts[:search_path]
680
1017
  case search_path
681
1018
  when String
682
1019
  search_path = search_path.split(",").map(&:strip)
@@ -711,37 +1048,13 @@ module Sequel
711
1048
  end
712
1049
  end
713
1050
 
714
- # Callback used when conversion procs are updated.
715
- def conversion_procs_updated
716
- nil
717
- end
718
-
719
- # Convert the hash of named conversion procs into a hash a oid conversion procs.
720
- def convert_named_procs_to_procs(named_procs)
721
- h = {}
722
- from(:pg_type).where(:typtype=>['b', 'e'], :typname=>named_procs.keys.map(&:to_s)).select_map([:oid, :typname]).each do |oid, name|
723
- h[oid.to_i] = named_procs[name.untaint.to_sym]
724
- end
725
- h
726
- end
727
-
728
- # Copy the conversion procs related to the given oids from PG_TYPES into
729
- # the conversion procs for this instance.
730
- def copy_conversion_procs(oids)
731
- procs = conversion_procs
732
- oids.each do |oid|
733
- procs[oid] = PG_TYPES[oid]
734
- end
735
- conversion_procs_updated
736
- end
737
-
738
- EXCLUSION_CONSTRAINT_SQL_STATE = '23P01'.freeze
739
- DEADLOCK_SQL_STATE = '40P01'.freeze
740
1051
  def database_specific_error_class_from_sqlstate(sqlstate)
741
- if sqlstate == EXCLUSION_CONSTRAINT_SQL_STATE
1052
+ if sqlstate == '23P01'
742
1053
  ExclusionConstraintViolation
743
- elsif sqlstate == DEADLOCK_SQL_STATE
1054
+ elsif sqlstate == '40P01'
744
1055
  SerializationFailure
1056
+ elsif sqlstate == '55P03'
1057
+ DatabaseLockTimeout
745
1058
  else
746
1059
  super
747
1060
  end
@@ -757,6 +1070,7 @@ module Sequel
757
1070
  [/violates not-null constraint/, NotNullConstraintViolation],
758
1071
  [/conflicting key value violates exclusion constraint/, ExclusionConstraintViolation],
759
1072
  [/could not serialize access/, SerializationFailure],
1073
+ [/could not obtain lock on row in relation/, DatabaseLockTimeout],
760
1074
  ].freeze
761
1075
  def database_error_regexps
762
1076
  DATABASE_ERROR_REGEXPS
@@ -814,6 +1128,7 @@ module Sequel
814
1128
  #{opts[:behavior].to_s.upcase if opts[:behavior]}
815
1129
  #{'STRICT' if opts[:strict]}
816
1130
  #{'SECURITY DEFINER' if opts[:security_definer]}
1131
+ #{"PARALLEL #{opts[:parallel].to_s.upcase}" if opts[:parallel]}
817
1132
  #{"COST #{opts[:cost]}" if opts[:cost]}
818
1133
  #{"ROWS #{opts[:rows]}" if opts[:rows]}
819
1134
  #{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
@@ -826,6 +1141,36 @@ module Sequel
826
1141
  "CREATE#{' OR REPLACE' if opts[:replace] && server_version >= 90000}#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
827
1142
  end
828
1143
 
1144
+ # Create a partition of another table, used when the create_table with
1145
+ # the :partition_of option is given.
1146
+ def create_partition_of_table_from_generator(name, generator, options)
1147
+ execute_ddl(create_partition_of_table_sql(name, generator, options))
1148
+ end
1149
+
1150
+ # SQL for creating a partition of another table.
1151
+ def create_partition_of_table_sql(name, generator, options)
1152
+ sql = create_table_prefix_sql(name, options).dup
1153
+
1154
+ sql << " PARTITION OF #{quote_schema_table(options[:partition_of])}"
1155
+
1156
+ case generator.partition_type
1157
+ when :range
1158
+ from, to = generator.range
1159
+ sql << " FOR VALUES FROM #{literal(from)} TO #{literal(to)}"
1160
+ when :list
1161
+ sql << " FOR VALUES IN #{literal(generator.list)}"
1162
+ when :hash
1163
+ mod, remainder = generator.hash_values
1164
+ sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
1165
+ else # when :default
1166
+ sql << " DEFAULT"
1167
+ end
1168
+
1169
+ sql << create_table_suffix_sql(name, options)
1170
+
1171
+ sql
1172
+ end
1173
+
829
1174
  # SQL for creating a schema.
830
1175
  def create_schema_sql(name, opts=OPTS)
831
1176
  "CREATE SCHEMA #{'IF NOT EXISTS ' if opts[:if_not_exists]}#{quote_identifier(name)}#{" AUTHORIZATION #{literal(opts[:owner])}" if opts[:owner]}"
@@ -841,27 +1186,42 @@ module Sequel
841
1186
  raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
842
1187
  'FOREIGN '
843
1188
  elsif options[:unlogged]
844
- UNLOGGED
1189
+ 'UNLOGGED '
845
1190
  end
846
1191
 
847
1192
  "CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
848
1193
  end
849
1194
 
1195
+ # SQL for creating a table with PostgreSQL specific options
850
1196
  def create_table_sql(name, generator, options)
851
- sql = super
1197
+ "#{super}#{create_table_suffix_sql(name, options)}"
1198
+ end
1199
+
1200
+ # Handle various PostgreSQl specific table extensions such as inheritance,
1201
+ # partitioning, tablespaces, and foreign tables.
1202
+ def create_table_suffix_sql(name, options)
1203
+ sql = String.new
852
1204
 
853
1205
  if inherits = options[:inherits]
854
- sql += " INHERITS (#{Array(inherits).map{|t| quote_schema_table(t)}.join(', ')})"
1206
+ sql << " INHERITS (#{Array(inherits).map{|t| quote_schema_table(t)}.join(', ')})"
1207
+ end
1208
+
1209
+ if partition_by = options[:partition_by]
1210
+ sql << " PARTITION BY #{options[:partition_type]||'RANGE'} #{literal(Array(partition_by))}"
855
1211
  end
856
1212
 
857
1213
  if on_commit = options[:on_commit]
858
1214
  raise(Error, "can't provide :on_commit without :temp to create_table") unless options[:temp]
859
1215
  raise(Error, "unsupported on_commit option: #{on_commit.inspect}") unless ON_COMMIT.has_key?(on_commit)
860
- sql += " ON COMMIT #{ON_COMMIT[on_commit]}"
1216
+ sql << " ON COMMIT #{ON_COMMIT[on_commit]}"
1217
+ end
1218
+
1219
+ if tablespace = options[:tablespace]
1220
+ sql << " TABLESPACE #{quote_identifier(tablespace)}"
861
1221
  end
862
1222
 
863
1223
  if server = options[:foreign]
864
- sql += " SERVER #{quote_identifier(server)}"
1224
+ sql << " SERVER #{quote_identifier(server)}"
865
1225
  if foreign_opts = options[:options]
866
1226
  sql << " OPTIONS (#{foreign_opts.map{|k, v| "#{k} #{literal(v.to_s)}"}.join(', ')})"
867
1227
  end
@@ -878,7 +1238,6 @@ module Sequel
878
1238
  result += " AS #{sql}"
879
1239
  end
880
1240
 
881
- # Use a PostgreSQL-specific create table generator
882
1241
  def create_table_generator_class
883
1242
  Postgres::CreateTableGenerator
884
1243
  end
@@ -891,17 +1250,22 @@ module Sequel
891
1250
  raise Error, "Trigger conditions are not supported for this database" unless supports_trigger_conditions?
892
1251
  filter = " WHEN #{filter_expr(filter)}"
893
1252
  end
894
- "CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
1253
+ "CREATE #{'OR REPLACE ' if opts[:replace]}TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
895
1254
  end
896
1255
 
897
1256
  # DDL fragment for initial part of CREATE VIEW statement
898
1257
  def create_view_prefix_sql(name, options)
899
- create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
900
- end
1258
+ sql = create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
901
1259
 
902
- # The errors that the main adapters can raise, depends on the adapter being used
903
- def database_error_classes
904
- CONVERTED_EXCEPTIONS
1260
+ if options[:security_invoker]
1261
+ sql += " WITH (security_invoker)"
1262
+ end
1263
+
1264
+ if tablespace = options[:tablespace]
1265
+ sql += " TABLESPACE #{quote_identifier(tablespace)}"
1266
+ end
1267
+
1268
+ sql
905
1269
  end
906
1270
 
907
1271
  # SQL for dropping a function from the database.
@@ -940,48 +1304,34 @@ module Sequel
940
1304
  "DROP #{'MATERIALIZED ' if opts[:materialized]}VIEW#{' IF EXISTS' if opts[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if opts[:cascade]}"
941
1305
  end
942
1306
 
943
- # If opts includes a :schema option, or a default schema is used, restrict the dataset to
944
- # that schema. Otherwise, just exclude the default PostgreSQL schemas except for public.
1307
+ # If opts includes a :schema option, use it, otherwise restrict the filter to only the
1308
+ # currently visible schemas.
945
1309
  def filter_schema(ds, opts)
946
1310
  expr = if schema = opts[:schema]
947
1311
  schema.to_s
948
1312
  else
949
1313
  Sequel.function(:any, Sequel.function(:current_schemas, false))
950
1314
  end
951
- ds.where(:pg_namespace__nspname=>expr)
952
- end
953
-
954
- # Return a hash with oid keys and callable values, used for converting types.
955
- def get_conversion_procs
956
- procs = PG_TYPES.dup
957
- procs[1184] = procs[1114] = method(:to_application_timestamp)
958
- add_named_conversion_procs(procs, PG_NAMED_TYPES)
959
- procs
1315
+ ds.where{{pg_namespace[:nspname]=>expr}}
960
1316
  end
961
1317
 
962
- # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
963
- def identifier_input_method_default
964
- nil
965
- end
966
-
967
- # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
968
- def identifier_output_method_default
969
- nil
970
- end
971
-
972
- # PostgreSQL specific index SQL.
973
1318
  def index_definition_sql(table_name, index)
974
1319
  cols = index[:columns]
975
1320
  index_name = index[:name] || default_index_name(table_name, cols)
1321
+
976
1322
  expr = if o = index[:opclass]
977
1323
  "(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
978
1324
  else
979
1325
  literal(Array(cols))
980
1326
  end
1327
+
1328
+ if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
981
1329
  unique = "UNIQUE " if index[:unique]
982
1330
  index_type = index[:type]
983
1331
  filter = index[:where] || index[:filter]
984
1332
  filter = " WHERE #{filter_expr(filter)}" if filter
1333
+ nulls_distinct = " NULLS#{' NOT' if index[:nulls_distinct] == false} DISTINCT" unless index[:nulls_distinct].nil?
1334
+
985
1335
  case index_type
986
1336
  when :full_text
987
1337
  expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
@@ -989,37 +1339,33 @@ module Sequel
989
1339
  when :spatial
990
1340
  index_type = :gist
991
1341
  end
992
- "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
1342
+
1343
+ "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
993
1344
  end
994
1345
 
995
1346
  # Setup datastructures shared by all postgres adapters.
996
1347
  def initialize_postgres_adapter
997
1348
  @primary_keys = {}
998
1349
  @primary_key_sequences = {}
999
- @conversion_procs = PG_TYPES.dup
1000
- reset_conversion_procs
1350
+ @supported_types = {}
1351
+ procs = @conversion_procs = CONVERSION_PROCS.dup
1352
+ procs[1184] = procs[1114] = method(:to_application_timestamp)
1001
1353
  end
1002
1354
 
1003
1355
  # Backbone of the tables and views support.
1004
1356
  def pg_class_relname(type, opts)
1005
- ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
1357
+ ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
1006
1358
  ds = filter_schema(ds, opts)
1007
1359
  m = output_identifier_meth
1008
- if block_given?
1360
+ if defined?(yield)
1009
1361
  yield(ds)
1010
1362
  elsif opts[:qualify]
1011
- ds.select_append(:pg_namespace__nspname).map{|r| Sequel.qualify(m.call(r[:nspname]), m.call(r[:relname]))}
1363
+ ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
1012
1364
  else
1013
1365
  ds.map{|r| m.call(r[:relname])}
1014
1366
  end
1015
1367
  end
1016
1368
 
1017
- # Use a dollar sign instead of question mark for the argument
1018
- # placeholder.
1019
- def prepared_arg_placeholder
1020
- PREPARED_ARG_PLACEHOLDER
1021
- end
1022
-
1023
1369
  # Return an expression the oid for the table expr. Used by the metadata parsing
1024
1370
  # code to disambiguate unqualified tables.
1025
1371
  def regclass_oid(expr, opts=OPTS)
@@ -1042,8 +1388,7 @@ module Sequel
1042
1388
  Sequel.cast(expr.to_s,:regclass).cast(:oid)
1043
1389
  end
1044
1390
 
1045
- # Remove the cached entries for primary keys and sequences when a table is
1046
- # changed.
1391
+ # Remove the cached entries for primary keys and sequences when a table is changed.
1047
1392
  def remove_cached_schema(table)
1048
1393
  tab = quote_schema_table(table)
1049
1394
  Sequel.synchronize do
@@ -1059,7 +1404,6 @@ module Sequel
1059
1404
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
1060
1405
  end
1061
1406
 
1062
- # Recognize PostgreSQL interval type.
1063
1407
  def schema_column_type(db_type)
1064
1408
  case db_type
1065
1409
  when /\Ainterval\z/io
@@ -1074,24 +1418,39 @@ module Sequel
1074
1418
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
1075
1419
  def schema_parse_table(table_name, opts)
1076
1420
  m = output_identifier_meth(opts[:dataset])
1077
- ds = metadata_dataset.select(:pg_attribute__attname___name,
1078
- SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
1079
- SQL::Cast.new(:basetype__oid, :integer).as(:base_oid),
1080
- SQL::Function.new(:format_type, :basetype__oid, :pg_type__typtypmod).as(:db_base_type),
1081
- SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
1082
- SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
1083
- SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
1084
- SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)).
1421
+ oid = regclass_oid(table_name, opts)
1422
+ ds = metadata_dataset.select{[
1423
+ pg_attribute[:attname].as(:name),
1424
+ SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1425
+ SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1426
+ SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1427
+ SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1428
+ SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
1429
+ SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
1430
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
1085
1431
  from(:pg_class).
1086
1432
  join(:pg_attribute, :attrelid=>:oid).
1087
1433
  join(:pg_type, :oid=>:atttypid).
1088
- left_outer_join(:pg_type___basetype, :oid=>:typbasetype).
1089
- left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
1090
- left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
1091
- filter(:pg_attribute__attisdropped=>false).
1092
- filter{|o| o.pg_attribute__attnum > 0}.
1093
- filter(:pg_class__oid=>regclass_oid(table_name, opts)).
1094
- order(:pg_attribute__attnum)
1434
+ left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
1435
+ left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
1436
+ left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
1437
+ where{{pg_attribute[:attisdropped]=>false}}.
1438
+ where{pg_attribute[:attnum] > 0}.
1439
+ where{{pg_class[:oid]=>oid}}.
1440
+ order{pg_attribute[:attnum]}
1441
+
1442
+ # :nocov:
1443
+ if server_version > 100000
1444
+ # :nocov:
1445
+ ds = ds.select_append{pg_attribute[:attidentity]}
1446
+
1447
+ # :nocov:
1448
+ if server_version > 120000
1449
+ # :nocov:
1450
+ ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
1451
+ end
1452
+ end
1453
+
1095
1454
  ds.map do |row|
1096
1455
  row[:default] = nil if blank_object?(row[:default])
1097
1456
  if row[:base_oid]
@@ -1104,8 +1463,9 @@ module Sequel
1104
1463
  row.delete(:db_base_type)
1105
1464
  end
1106
1465
  row[:type] = schema_column_type(row[:db_type])
1466
+ identity = row.delete(:attidentity)
1107
1467
  if row[:primary_key]
1108
- row[:auto_increment] = !!(row[:default] =~ /\Anextval/io)
1468
+ row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
1109
1469
  end
1110
1470
  [m.call(row.delete(:name)), row]
1111
1471
  end
@@ -1159,12 +1519,14 @@ module Sequel
1159
1519
  # PostgreSQL prefers the text datatype. If a fixed size is requested,
1160
1520
  # the char type is used. If the text type is specifically
1161
1521
  # disallowed or there is a size specified, use the varchar type.
1162
- # Otherwise use the type type.
1522
+ # Otherwise use the text type.
1163
1523
  def type_literal_generic_string(column)
1164
- if column[:fixed]
1165
- "char(#{column[:size]||255})"
1166
- elsif column[:text] == false or column[:size]
1167
- "varchar(#{column[:size]||255})"
1524
+ if column[:text]
1525
+ :text
1526
+ elsif column[:fixed]
1527
+ "char(#{column[:size]||default_string_column_size})"
1528
+ elsif column[:text] == false || column[:size]
1529
+ "varchar(#{column[:size]||default_string_column_size})"
1168
1530
  else
1169
1531
  :text
1170
1532
  end
@@ -1172,77 +1534,23 @@ module Sequel
1172
1534
 
1173
1535
  # PostgreSQL 9.4+ supports views with check option.
1174
1536
  def view_with_check_option_support
1537
+ # :nocov:
1175
1538
  :local if server_version >= 90400
1539
+ # :nocov:
1176
1540
  end
1177
1541
  end
1178
1542
 
1179
- # Instance methods for datasets that connect to a PostgreSQL database.
1180
1543
  module DatasetMethods
1181
- ACCESS_SHARE = 'ACCESS SHARE'.freeze
1182
- ACCESS_EXCLUSIVE = 'ACCESS EXCLUSIVE'.freeze
1183
- BOOL_FALSE = 'false'.freeze
1184
- BOOL_TRUE = 'true'.freeze
1185
- COMMA_SEPARATOR = ', '.freeze
1186
- EXCLUSIVE = 'EXCLUSIVE'.freeze
1187
- EXPLAIN = 'EXPLAIN '.freeze
1188
- EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
1189
- FOR_SHARE = ' FOR SHARE'.freeze
1544
+ include UnmodifiedIdentifiers::DatasetMethods
1545
+
1190
1546
  NULL = LiteralString.new('NULL').freeze
1191
- PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
1192
- QUERY_PLAN = 'QUERY PLAN'.to_sym
1193
- ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
1194
- ROW_SHARE = 'ROW SHARE'.freeze
1195
- SHARE = 'SHARE'.freeze
1196
- SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
1197
- SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
1198
- SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
1199
- SPACE = Dataset::SPACE
1200
- FROM = Dataset::FROM
1201
- APOS = Dataset::APOS
1202
- APOS_RE = Dataset::APOS_RE
1203
- DOUBLE_APOS = Dataset::DOUBLE_APOS
1204
- PAREN_OPEN = Dataset::PAREN_OPEN
1205
- PAREN_CLOSE = Dataset::PAREN_CLOSE
1206
- COMMA = Dataset::COMMA
1207
- ESCAPE = Dataset::ESCAPE
1208
- BACKSLASH = Dataset::BACKSLASH
1209
- AS = Dataset::AS
1210
- XOR_OP = ' # '.freeze
1211
- CRLF = "\r\n".freeze
1212
- BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
1213
- WINDOW = " WINDOW ".freeze
1214
- SELECT_VALUES = "VALUES ".freeze
1215
- EMPTY_STRING = ''.freeze
1216
- LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze)
1217
- SKIP_LOCKED = " SKIP LOCKED".freeze
1547
+ LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
1218
1548
 
1219
1549
  Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
1220
- Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
1550
+ Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns override values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
1221
1551
  Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
1222
1552
  Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
1223
1553
 
1224
- # Shared methods for prepared statements when used with PostgreSQL databases.
1225
- module PreparedStatementMethods
1226
- # Override insert action to use RETURNING if the server supports it.
1227
- def run
1228
- if @prepared_type == :insert && (opts[:returning_pk] || !opts[:returning])
1229
- fetch_rows(prepared_sql){|r| return r.values.first}
1230
- else
1231
- super
1232
- end
1233
- end
1234
-
1235
- def prepared_sql
1236
- return @prepared_sql if @prepared_sql
1237
- if @prepared_type == :insert && !opts[:returning]
1238
- @opts[:returning] = insert_pk
1239
- @opts[:returning_pk] = true
1240
- end
1241
- super
1242
- @prepared_sql
1243
- end
1244
- end
1245
-
1246
1554
  # Return the results of an EXPLAIN ANALYZE query as a string
1247
1555
  def analyze
1248
1556
  explain(:analyze=>true)
@@ -1254,7 +1562,7 @@ module Sequel
1254
1562
  def complex_expression_sql_append(sql, op, args)
1255
1563
  case op
1256
1564
  when :^
1257
- j = XOR_OP
1565
+ j = ' # '
1258
1566
  c = false
1259
1567
  args.each do |a|
1260
1568
  sql << j if c
@@ -1262,13 +1570,13 @@ module Sequel
1262
1570
  c ||= true
1263
1571
  end
1264
1572
  when :ILIKE, :'NOT ILIKE'
1265
- sql << PAREN_OPEN
1266
- literal_append(sql, args.at(0))
1267
- sql << SPACE << op.to_s << SPACE
1268
- literal_append(sql, args.at(1))
1269
- sql << ESCAPE
1270
- literal_append(sql, BACKSLASH)
1271
- sql << PAREN_CLOSE
1573
+ sql << '('
1574
+ literal_append(sql, args[0])
1575
+ sql << ' ' << op.to_s << ' '
1576
+ literal_append(sql, args[1])
1577
+ sql << " ESCAPE "
1578
+ literal_append(sql, "\\")
1579
+ sql << ')'
1272
1580
  else
1273
1581
  super
1274
1582
  end
@@ -1294,7 +1602,7 @@ module Sequel
1294
1602
 
1295
1603
  # Return the results of an EXPLAIN query as a string
1296
1604
  def explain(opts=OPTS)
1297
- with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
1605
+ with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
1298
1606
  end
1299
1607
 
1300
1608
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
@@ -1315,6 +1623,8 @@ module Sequel
1315
1623
  # :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
1316
1624
  # returned rows also include the exact phrase used.
1317
1625
  # :rank :: Set to true to order by the rank, so that closer matches are returned first.
1626
+ # :to_tsquery :: Can be set to :plain or :phrase to specify the function to use to
1627
+ # convert the terms to a ts_query.
1318
1628
  # :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
1319
1629
  # tsquery, and can be used directly in the query.
1320
1630
  # :tsvector :: Specifies the cols argument is already a valid SQL expression returning a
@@ -1329,11 +1639,18 @@ module Sequel
1329
1639
 
1330
1640
  unless opts[:tsquery]
1331
1641
  phrase_terms = terms.is_a?(Array) ? terms.join(' | ') : terms
1332
- query_func = (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1642
+
1643
+ query_func = case to_tsquery = opts[:to_tsquery]
1644
+ when :phrase, :plain
1645
+ :"#{to_tsquery}to_tsquery"
1646
+ else
1647
+ (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1648
+ end
1649
+
1333
1650
  terms = Sequel.function(query_func, lang, phrase_terms)
1334
1651
  end
1335
1652
 
1336
- ds = where(Sequel.lit(["(", " @@ ", ")"], cols, terms))
1653
+ ds = where(Sequel.lit(["", " @@ ", ""], cols, terms))
1337
1654
 
1338
1655
  if opts[:phrase]
1339
1656
  raise Error, "can't use :phrase with either :tsvector or :tsquery arguments to full_text_search together" if opts[:tsvector] || opts[:tsquery]
@@ -1370,6 +1687,7 @@ module Sequel
1370
1687
 
1371
1688
  # Handle uniqueness violations when inserting, by updating the conflicting row, using
1372
1689
  # ON CONFLICT. With no options, uses ON CONFLICT DO NOTHING. Options:
1690
+ # :conflict_where :: The index filter, when using a partial index to determine uniqueness.
1373
1691
  # :constraint :: An explicit constraint name, has precendence over :target.
1374
1692
  # :target :: The column name or expression to handle uniqueness violations on.
1375
1693
  # :update :: A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
@@ -1377,24 +1695,28 @@ module Sequel
1377
1695
  #
1378
1696
  # Examples:
1379
1697
  #
1380
- # DB[:table].insert_conflict.insert(:a=>1, :b=>2)
1698
+ # DB[:table].insert_conflict.insert(a: 1, b: 2)
1381
1699
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1382
1700
  # # ON CONFLICT DO NOTHING
1383
1701
  #
1384
- # DB[:table].insert_conflict(:constraint=>:table_a_uidx).insert(:a=>1, :b=>2)
1702
+ # DB[:table].insert_conflict(constraint: :table_a_uidx).insert(a: 1, b: 2)
1385
1703
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1386
1704
  # # ON CONFLICT ON CONSTRAINT table_a_uidx DO NOTHING
1387
1705
  #
1388
- # DB[:table].insert_conflict(:target=>:a).insert(:a=>1, :b=>2)
1706
+ # DB[:table].insert_conflict(target: :a).insert(a: 1, b: 2)
1389
1707
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1390
1708
  # # ON CONFLICT (a) DO NOTHING
1709
+ #
1710
+ # DB[:table].insert_conflict(target: :a, conflict_where: {c: true}).insert(a: 1, b: 2)
1711
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1712
+ # # ON CONFLICT (a) WHERE (c IS TRUE) DO NOTHING
1391
1713
  #
1392
- # DB[:table].insert_conflict(:target=>:a, :update=>{:b=>:excluded__b}).insert(:a=>1, :b=>2)
1714
+ # DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
1393
1715
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1394
1716
  # # ON CONFLICT (a) DO UPDATE SET b = excluded.b
1395
1717
  #
1396
- # DB[:table].insert_conflict(:constraint=>:table_a_uidx,
1397
- # :update=>{:b=>:excluded__b}, :update_where=>{:table__status_id=>1}).insert(:a=>1, :b=>2)
1718
+ # DB[:table].insert_conflict(constraint: :table_a_uidx,
1719
+ # update: {b: Sequel[:excluded][:b]}, update_where: {Sequel[:table][:status_id] => 1}).insert(a: 1, b: 2)
1398
1720
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1399
1721
  # # ON CONFLICT ON CONSTRAINT table_a_uidx
1400
1722
  # # DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
@@ -1405,18 +1727,20 @@ module Sequel
1405
1727
  # Ignore uniqueness/exclusion violations when inserting, using ON CONFLICT DO NOTHING.
1406
1728
  # Exists mostly for compatibility to MySQL's insert_ignore. Example:
1407
1729
  #
1408
- # DB[:table].insert_ignore.insert(:a=>1, :b=>2)
1730
+ # DB[:table].insert_ignore.insert(a: 1, b: 2)
1409
1731
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1410
1732
  # # ON CONFLICT DO NOTHING
1411
1733
  def insert_ignore
1412
1734
  insert_conflict
1413
1735
  end
1414
1736
 
1415
- # Insert a record returning the record inserted. Always returns nil without
1416
- # inserting a query if disable_insert_returning is used.
1737
+ # Insert a record, returning the record inserted, using RETURNING. Always returns nil without
1738
+ # running an INSERT statement if disable_insert_returning is used. If the query runs
1739
+ # but returns no values, returns false.
1417
1740
  def insert_select(*values)
1418
1741
  return unless supports_insert_select?
1419
- server?(:default).with_sql_first(insert_select_sql(*values))
1742
+ # Handle case where query does not return a row
1743
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
1420
1744
  end
1421
1745
 
1422
1746
  # The SQL to use for an insert_select, adds a RETURNING clause to the insert
@@ -1426,13 +1750,22 @@ module Sequel
1426
1750
  ds.insert_sql(*values)
1427
1751
  end
1428
1752
 
1753
+ # Support SQL::AliasedExpression as expr to setup a USING join with a table alias for the
1754
+ # USING columns.
1755
+ def join_table(type, table, expr=nil, options=OPTS, &block)
1756
+ if expr.is_a?(SQL::AliasedExpression) && expr.expression.is_a?(Array) && !expr.expression.empty? && expr.expression.all?
1757
+ options = options.merge(:join_using=>true)
1758
+ end
1759
+ super
1760
+ end
1761
+
1429
1762
  # Locks all tables in the dataset's FROM clause (but not in JOINs) with
1430
1763
  # the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
1431
- # a new transaction, locks the table, and yields. If a block is not given
1764
+ # a new transaction, locks the table, and yields. If a block is not given,
1432
1765
  # just locks the tables. Note that PostgreSQL will probably raise an error
1433
1766
  # if you lock the table outside of an existing transaction. Returns nil.
1434
1767
  def lock(mode, opts=OPTS)
1435
- if block_given? # perform locking inside a transaction and yield to block
1768
+ if defined?(yield) # perform locking inside a transaction and yield to block
1436
1769
  @db.transaction(opts){lock(mode, opts); yield}
1437
1770
  else
1438
1771
  sql = 'LOCK TABLE '.dup
@@ -1447,6 +1780,54 @@ module Sequel
1447
1780
  nil
1448
1781
  end
1449
1782
 
1783
+ # Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
1784
+ # MERGE statement. If a block is passed, treat it as a virtual row and
1785
+ # use it as additional conditions for the match.
1786
+ #
1787
+ # merge_do_nothing_when_matched
1788
+ # # WHEN MATCHED THEN DO NOTHING
1789
+ #
1790
+ # merge_do_nothing_when_matched{a > 30}
1791
+ # # WHEN MATCHED AND (a > 30) THEN DO NOTHING
1792
+ def merge_do_nothing_when_matched(&block)
1793
+ _merge_when(:type=>:matched, &block)
1794
+ end
1795
+
1796
+ # Return a dataset with a WHEN NOT MATCHED THEN DO NOTHING clause added to the
1797
+ # MERGE statement. If a block is passed, treat it as a virtual row and
1798
+ # use it as additional conditions for the match.
1799
+ #
1800
+ # merge_do_nothing_when_not_matched
1801
+ # # WHEN NOT MATCHED THEN DO NOTHING
1802
+ #
1803
+ # merge_do_nothing_when_not_matched{a > 30}
1804
+ # # WHEN NOT MATCHED AND (a > 30) THEN DO NOTHING
1805
+ def merge_do_nothing_when_not_matched(&block)
1806
+ _merge_when(:type=>:not_matched, &block)
1807
+ end
1808
+
1809
+ # Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
1810
+ def merge_insert(*values, &block)
1811
+ h = {:type=>:insert, :values=>values}
1812
+ if override = @opts[:override]
1813
+ h[:override] = insert_override_sql(String.new)
1814
+ end
1815
+ _merge_when(h, &block)
1816
+ end
1817
+
1818
+ # Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
1819
+ # always use the user supplied value, and an error is not raised for identity
1820
+ # columns that are GENERATED ALWAYS.
1821
+ def overriding_system_value
1822
+ clone(:override=>:system)
1823
+ end
1824
+
1825
+ # Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
1826
+ # always use the sequence value instead of the user supplied value.
1827
+ def overriding_user_value
1828
+ clone(:override=>:user)
1829
+ end
1830
+
1450
1831
  def supports_cte?(type=:select)
1451
1832
  if type == :select
1452
1833
  server_version >= 80400
@@ -1491,7 +1872,7 @@ module Sequel
1491
1872
  server_version >= 90500
1492
1873
  end
1493
1874
 
1494
- # PostgreSQL 9.3rc1+ supports lateral subqueries
1875
+ # PostgreSQL 9.3+ supports lateral subqueries
1495
1876
  def supports_lateral_subqueries?
1496
1877
  server_version >= 90300
1497
1878
  end
@@ -1501,6 +1882,16 @@ module Sequel
1501
1882
  true
1502
1883
  end
1503
1884
 
1885
+ # PostgreSQL 15+ supports MERGE.
1886
+ def supports_merge?
1887
+ server_version >= 150000
1888
+ end
1889
+
1890
+ # PostgreSQL supports NOWAIT.
1891
+ def supports_nowait?
1892
+ true
1893
+ end
1894
+
1504
1895
  # Returning is always supported.
1505
1896
  def supports_returning?(type)
1506
1897
  true
@@ -1521,11 +1912,31 @@ module Sequel
1521
1912
  true
1522
1913
  end
1523
1914
 
1915
+ # PostgreSQL 8.4+ supports WINDOW clause.
1916
+ def supports_window_clause?
1917
+ server_version >= 80400
1918
+ end
1919
+
1524
1920
  # PostgreSQL 8.4+ supports window functions
1525
1921
  def supports_window_functions?
1526
1922
  server_version >= 80400
1527
1923
  end
1528
1924
 
1925
+ # Base support added in 8.4, offset supported added in 9.0,
1926
+ # GROUPS and EXCLUDE support added in 11.0.
1927
+ def supports_window_function_frame_option?(option)
1928
+ case option
1929
+ when :rows, :range
1930
+ true
1931
+ when :offset
1932
+ server_version >= 90000
1933
+ when :groups, :exclude
1934
+ server_version >= 110000
1935
+ else
1936
+ false
1937
+ end
1938
+ end
1939
+
1529
1940
  # Truncates the dataset. Returns nil.
1530
1941
  #
1531
1942
  # Options:
@@ -1537,10 +1948,11 @@ module Sequel
1537
1948
  # :only and :restart only work correctly on PostgreSQL 8.4+.
1538
1949
  #
1539
1950
  # Usage:
1540
- # DB[:table].truncate # TRUNCATE TABLE "table"
1541
- # # => nil
1542
- # DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
1543
- # # => nil
1951
+ # DB[:table].truncate
1952
+ # # TRUNCATE TABLE "table"
1953
+ #
1954
+ # DB[:table].truncate(cascade: true, only: true, restart: true)
1955
+ # # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
1544
1956
  def truncate(opts = OPTS)
1545
1957
  if opts.empty?
1546
1958
  super()
@@ -1549,9 +1961,11 @@ module Sequel
1549
1961
  end
1550
1962
  end
1551
1963
 
1552
- # Return a clone of the dataset with an addition named window that can be referenced in window functions.
1553
- def window(name, opts)
1554
- clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
1964
+ # Use WITH TIES when limiting the result set to also include additional
1965
+ # rules that have the same results for the order column as the final row.
1966
+ # Requires PostgreSQL 13.
1967
+ def with_ties
1968
+ clone(:limit_with_ties=>true)
1555
1969
  end
1556
1970
 
1557
1971
  protected
@@ -1563,7 +1977,9 @@ module Sequel
1563
1977
  def _import(columns, values, opts=OPTS)
1564
1978
  if @opts[:returning]
1565
1979
  statements = multi_insert_sql(columns, values)
1566
- @db.transaction(Hash[opts].merge!(:server=>@opts[:server])) do
1980
+ trans_opts = Hash[opts]
1981
+ trans_opts[:server] = @opts[:server]
1982
+ @db.transaction(trans_opts) do
1567
1983
  statements.map{|st| returning_fetch_rows(st)}
1568
1984
  end.first.map{|v| v.length == 1 ? v.values.first : v}
1569
1985
  elsif opts[:return] == :primary_key
@@ -1573,11 +1989,35 @@ module Sequel
1573
1989
  end
1574
1990
  end
1575
1991
 
1992
+ def to_prepared_statement(type, *a)
1993
+ if type == :insert && !@opts.has_key?(:returning)
1994
+ returning(insert_pk).send(:to_prepared_statement, :insert_pk, *a)
1995
+ else
1996
+ super
1997
+ end
1998
+ end
1999
+
1576
2000
  private
1577
2001
 
2002
+ # Append the INSERT sql used in a MERGE
2003
+ def _merge_insert_sql(sql, data)
2004
+ sql << " THEN INSERT "
2005
+ columns, values = _parse_insert_sql_args(data[:values])
2006
+ _insert_columns_sql(sql, columns)
2007
+ if override = data[:override]
2008
+ sql << override
2009
+ end
2010
+ _insert_values_sql(sql, values)
2011
+ end
2012
+
2013
+ def _merge_matched_sql(sql, data)
2014
+ sql << " THEN DO NOTHING"
2015
+ end
2016
+ alias _merge_not_matched_sql _merge_matched_sql
2017
+
1578
2018
  # Format TRUNCATE statement with PostgreSQL specific options.
1579
2019
  def _truncate_sql(table)
1580
- to = @opts[:truncate_opts] || {}
2020
+ to = @opts[:truncate_opts] || OPTS
1581
2021
  "TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
1582
2022
  end
1583
2023
 
@@ -1589,7 +2029,7 @@ module Sequel
1589
2029
 
1590
2030
  # Only include the primary table in the main delete clause
1591
2031
  def delete_from_sql(sql)
1592
- sql << FROM
2032
+ sql << ' FROM '
1593
2033
  source_list_append(sql, @opts[:from][0..0])
1594
2034
  end
1595
2035
 
@@ -1609,54 +2049,87 @@ module Sequel
1609
2049
  elsif target = opts[:target]
1610
2050
  sql << ' '
1611
2051
  identifier_append(sql, Array(target))
2052
+ if conflict_where = opts[:conflict_where]
2053
+ sql << " WHERE "
2054
+ literal_append(sql, conflict_where)
2055
+ end
1612
2056
  end
1613
2057
 
1614
2058
  if values = opts[:update]
1615
2059
  sql << " DO UPDATE SET "
1616
2060
  update_sql_values_hash(sql, values)
1617
- if where = opts[:update_where]
2061
+ if update_where = opts[:update_where]
1618
2062
  sql << " WHERE "
1619
- literal_append(sql, where)
2063
+ literal_append(sql, update_where)
1620
2064
  end
1621
2065
  else
1622
2066
  sql << " DO NOTHING"
1623
2067
  end
2068
+ end
2069
+ end
1624
2070
 
2071
+ # Include aliases when inserting into a single table on PostgreSQL 9.5+.
2072
+ def insert_into_sql(sql)
2073
+ sql << " INTO "
2074
+ if (f = @opts[:from]) && f.length == 1
2075
+ identifier_append(sql, server_version >= 90500 ? f.first : unaliased_identifier(f.first))
2076
+ else
2077
+ source_list_append(sql, f)
1625
2078
  end
1626
2079
  end
1627
2080
 
1628
2081
  # Return the primary key to use for RETURNING in an INSERT statement
1629
2082
  def insert_pk
1630
- if (f = opts[:from]) && !f.empty?
1631
- case t = f.first
1632
- when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
1633
- if pk = db.primary_key(t)
1634
- Sequel::SQL::Identifier.new(pk)
1635
- end
2083
+ (f = opts[:from]) && !f.empty? && (t = f.first)
2084
+ case t
2085
+ when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
2086
+ if pk = db.primary_key(t)
2087
+ Sequel::SQL::Identifier.new(pk)
1636
2088
  end
1637
2089
  end
1638
2090
  end
1639
2091
 
2092
+ # Support OVERRIDING SYSTEM|USER VALUE in insert statements
2093
+ def insert_override_sql(sql)
2094
+ case opts[:override]
2095
+ when :system
2096
+ sql << " OVERRIDING SYSTEM VALUE"
2097
+ when :user
2098
+ sql << " OVERRIDING USER VALUE"
2099
+ end
2100
+ end
2101
+
1640
2102
  # For multiple table support, PostgreSQL requires at least
1641
2103
  # two from tables, with joins allowed.
1642
2104
  def join_from_sql(type, sql)
1643
2105
  if(from = @opts[:from][1..-1]).empty?
1644
2106
  raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
1645
2107
  else
1646
- sql << SPACE << type.to_s << SPACE
2108
+ sql << ' ' << type.to_s << ' '
1647
2109
  source_list_append(sql, from)
1648
2110
  select_join_sql(sql)
1649
2111
  end
1650
2112
  end
1651
2113
 
2114
+ # Support table aliases for USING columns
2115
+ def join_using_clause_using_sql_append(sql, using_columns)
2116
+ if using_columns.is_a?(SQL::AliasedExpression)
2117
+ super(sql, using_columns.expression)
2118
+ sql << ' AS '
2119
+ identifier_append(sql, using_columns.alias)
2120
+ else
2121
+ super
2122
+ end
2123
+ end
2124
+
1652
2125
  # Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
1653
2126
  def literal_blob_append(sql, v)
1654
- sql << APOS << v.gsub(BLOB_RE){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << APOS
2127
+ sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
1655
2128
  end
1656
2129
 
1657
2130
  # PostgreSQL uses FALSE for false values
1658
2131
  def literal_false
1659
- BOOL_FALSE
2132
+ 'false'
1660
2133
  end
1661
2134
 
1662
2135
  # PostgreSQL quotes NaN and Infinity.
@@ -1674,12 +2147,12 @@ module Sequel
1674
2147
 
1675
2148
  # Assume that SQL standard quoting is on, per Sequel's defaults
1676
2149
  def literal_string_append(sql, v)
1677
- sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
2150
+ sql << "'" << v.gsub("'", "''") << "'"
1678
2151
  end
1679
2152
 
1680
- # PostgreSQL uses FALSE for false values
2153
+ # PostgreSQL uses true for true values
1681
2154
  def literal_true
1682
- BOOL_TRUE
2155
+ 'true'
1683
2156
  end
1684
2157
 
1685
2158
  # PostgreSQL supports multiple rows in INSERT.
@@ -1687,54 +2160,116 @@ module Sequel
1687
2160
  :values
1688
2161
  end
1689
2162
 
2163
+ # Dataset options that do not affect the generated SQL.
2164
+ def non_sql_option?(key)
2165
+ super || key == :cursor || key == :insert_conflict
2166
+ end
2167
+
1690
2168
  # PostgreSQL requires parentheses around compound datasets if they use
1691
2169
  # CTEs, and using them in other places doesn't hurt.
1692
2170
  def compound_dataset_sql_append(sql, ds)
1693
- sql << PAREN_OPEN
2171
+ sql << '('
1694
2172
  super
1695
- sql << PAREN_CLOSE
2173
+ sql << ')'
2174
+ end
2175
+
2176
+ # Backslash is supported by default as the escape character on PostgreSQL,
2177
+ # and using ESCAPE can break LIKE ANY() usage.
2178
+ def requires_like_escape?
2179
+ false
2180
+ end
2181
+
2182
+ # Support FETCH FIRST WITH TIES on PostgreSQL 13+.
2183
+ def select_limit_sql(sql)
2184
+ l = @opts[:limit]
2185
+ o = @opts[:offset]
2186
+
2187
+ return unless l || o
2188
+
2189
+ if @opts[:limit_with_ties]
2190
+ if o
2191
+ sql << " OFFSET "
2192
+ literal_append(sql, o)
2193
+ end
2194
+
2195
+ if l
2196
+ sql << " FETCH FIRST "
2197
+ literal_append(sql, l)
2198
+ sql << " ROWS WITH TIES"
2199
+ end
2200
+ else
2201
+ if l
2202
+ sql << " LIMIT "
2203
+ literal_append(sql, l)
2204
+ end
2205
+
2206
+ if o
2207
+ sql << " OFFSET "
2208
+ literal_append(sql, o)
2209
+ end
2210
+ end
1696
2211
  end
1697
2212
 
1698
2213
  # Support FOR SHARE locking when using the :share lock style.
1699
2214
  # Use SKIP LOCKED if skipping locked rows.
1700
2215
  def select_lock_sql(sql)
1701
- if @opts[:lock] == :share
1702
- sql << FOR_SHARE
2216
+ lock = @opts[:lock]
2217
+ if lock == :share
2218
+ sql << ' FOR SHARE'
1703
2219
  else
1704
2220
  super
1705
2221
  end
1706
2222
 
1707
- if @opts[:skip_locked]
1708
- sql << SKIP_LOCKED
2223
+ if lock
2224
+ if @opts[:skip_locked]
2225
+ sql << " SKIP LOCKED"
2226
+ elsif @opts[:nowait]
2227
+ sql << " NOWAIT"
2228
+ end
1709
2229
  end
1710
2230
  end
1711
2231
 
1712
2232
  # Support VALUES clause instead of the SELECT clause to return rows.
1713
2233
  def select_values_sql(sql)
1714
- sql << SELECT_VALUES
2234
+ sql << "VALUES "
1715
2235
  expression_list_append(sql, opts[:values])
1716
2236
  end
1717
2237
 
1718
- # SQL fragment for named window specifications
1719
- def select_window_sql(sql)
1720
- if ws = @opts[:window]
1721
- sql << WINDOW
1722
- c = false
1723
- co = COMMA
1724
- as = AS
1725
- ws.map do |name, window|
1726
- sql << co if c
1727
- literal_append(sql, name)
1728
- sql << as
1729
- literal_append(sql, window)
1730
- c ||= true
2238
+ # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
2239
+ def select_with_sql_base
2240
+ opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
2241
+ end
2242
+
2243
+ # Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
2244
+ def select_with_sql_cte(sql, cte)
2245
+ super
2246
+
2247
+ if search_opts = cte[:search]
2248
+ sql << if search_opts[:type] == :breadth
2249
+ " SEARCH BREADTH FIRST BY "
2250
+ else
2251
+ " SEARCH DEPTH FIRST BY "
1731
2252
  end
2253
+
2254
+ identifier_list_append(sql, Array(search_opts[:by]))
2255
+ sql << " SET "
2256
+ identifier_append(sql, search_opts[:set] || :ordercol)
1732
2257
  end
1733
- end
1734
2258
 
1735
- # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
1736
- def select_with_sql_base
1737
- opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
2259
+ if cycle_opts = cte[:cycle]
2260
+ sql << " CYCLE "
2261
+ identifier_list_append(sql, Array(cycle_opts[:columns]))
2262
+ sql << " SET "
2263
+ identifier_append(sql, cycle_opts[:cycle_column] || :is_cycle)
2264
+ if cycle_opts.has_key?(:cycle_value)
2265
+ sql << " TO "
2266
+ literal_append(sql, cycle_opts[:cycle_value])
2267
+ sql << " DEFAULT "
2268
+ literal_append(sql, cycle_opts.fetch(:noncycle_value, false))
2269
+ end
2270
+ sql << " USING "
2271
+ identifier_append(sql, cycle_opts[:path_column] || :path)
2272
+ end
1738
2273
  end
1739
2274
 
1740
2275
  # The version of the database server
@@ -1742,6 +2277,11 @@ module Sequel
1742
2277
  db.server_version(@opts[:server])
1743
2278
  end
1744
2279
 
2280
+ # PostgreSQL 9.4+ supports the FILTER clause for aggregate functions.
2281
+ def supports_filtered_aggregates?
2282
+ server_version >= 90400
2283
+ end
2284
+
1745
2285
  # PostgreSQL supports quoted function names.
1746
2286
  def supports_quoted_function_names?
1747
2287
  true
@@ -1749,8 +2289,8 @@ module Sequel
1749
2289
 
1750
2290
  # Concatenate the expressions with a space in between
1751
2291
  def full_text_string_join(cols)
1752
- cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, EMPTY_STRING)}
1753
- cols = cols.zip([SPACE] * cols.length).flatten
2292
+ cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
2293
+ cols = cols.zip([' '] * cols.length).flatten
1754
2294
  cols.pop
1755
2295
  SQL::StringExpression.new(:'||', *cols)
1756
2296
  end
@@ -1762,7 +2302,7 @@ module Sequel
1762
2302
 
1763
2303
  # Only include the primary table in the main update clause
1764
2304
  def update_table_sql(sql)
1765
- sql << SPACE
2305
+ sql << ' '
1766
2306
  source_list_append(sql, @opts[:from][0..0])
1767
2307
  end
1768
2308
  end