sequel 4.26.0 → 5.37.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 (692) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG +405 -5656
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +232 -157
  5. data/bin/sequel +32 -9
  6. data/doc/advanced_associations.rdoc +252 -188
  7. data/doc/association_basics.rdoc +231 -273
  8. data/doc/bin_sequel.rdoc +5 -3
  9. data/doc/cheat_sheet.rdoc +75 -48
  10. data/doc/code_order.rdoc +28 -10
  11. data/doc/core_extensions.rdoc +104 -63
  12. data/doc/dataset_basics.rdoc +12 -21
  13. data/doc/dataset_filtering.rdoc +99 -86
  14. data/doc/extensions.rdoc +3 -10
  15. data/doc/mass_assignment.rdoc +74 -31
  16. data/doc/migration.rdoc +72 -46
  17. data/doc/model_dataset_method_design.rdoc +129 -0
  18. data/doc/model_hooks.rdoc +15 -25
  19. data/doc/model_plugins.rdoc +12 -12
  20. data/doc/mssql_stored_procedures.rdoc +3 -3
  21. data/doc/object_model.rdoc +59 -69
  22. data/doc/opening_databases.rdoc +84 -94
  23. data/doc/postgresql.rdoc +268 -38
  24. data/doc/prepared_statements.rdoc +29 -24
  25. data/doc/querying.rdoc +184 -164
  26. data/doc/reflection.rdoc +5 -6
  27. data/doc/release_notes/5.0.0.txt +159 -0
  28. data/doc/release_notes/5.1.0.txt +31 -0
  29. data/doc/release_notes/5.10.0.txt +84 -0
  30. data/doc/release_notes/5.11.0.txt +83 -0
  31. data/doc/release_notes/5.12.0.txt +141 -0
  32. data/doc/release_notes/5.13.0.txt +27 -0
  33. data/doc/release_notes/5.14.0.txt +63 -0
  34. data/doc/release_notes/5.15.0.txt +39 -0
  35. data/doc/release_notes/5.16.0.txt +110 -0
  36. data/doc/release_notes/5.17.0.txt +31 -0
  37. data/doc/release_notes/5.18.0.txt +69 -0
  38. data/doc/release_notes/5.19.0.txt +28 -0
  39. data/doc/release_notes/5.2.0.txt +33 -0
  40. data/doc/release_notes/5.20.0.txt +89 -0
  41. data/doc/release_notes/5.21.0.txt +87 -0
  42. data/doc/release_notes/5.22.0.txt +48 -0
  43. data/doc/release_notes/5.23.0.txt +56 -0
  44. data/doc/release_notes/5.24.0.txt +56 -0
  45. data/doc/release_notes/5.25.0.txt +32 -0
  46. data/doc/release_notes/5.26.0.txt +35 -0
  47. data/doc/release_notes/5.27.0.txt +21 -0
  48. data/doc/release_notes/5.28.0.txt +16 -0
  49. data/doc/release_notes/5.29.0.txt +22 -0
  50. data/doc/release_notes/5.3.0.txt +121 -0
  51. data/doc/release_notes/5.30.0.txt +20 -0
  52. data/doc/release_notes/5.31.0.txt +148 -0
  53. data/doc/release_notes/5.32.0.txt +46 -0
  54. data/doc/release_notes/5.33.0.txt +24 -0
  55. data/doc/release_notes/5.34.0.txt +40 -0
  56. data/doc/release_notes/5.35.0.txt +56 -0
  57. data/doc/release_notes/5.36.0.txt +60 -0
  58. data/doc/release_notes/5.37.0.txt +30 -0
  59. data/doc/release_notes/5.4.0.txt +80 -0
  60. data/doc/release_notes/5.5.0.txt +61 -0
  61. data/doc/release_notes/5.6.0.txt +31 -0
  62. data/doc/release_notes/5.7.0.txt +108 -0
  63. data/doc/release_notes/5.8.0.txt +170 -0
  64. data/doc/release_notes/5.9.0.txt +99 -0
  65. data/doc/schema_modification.rdoc +102 -77
  66. data/doc/security.rdoc +160 -87
  67. data/doc/sharding.rdoc +74 -47
  68. data/doc/sql.rdoc +135 -122
  69. data/doc/testing.rdoc +34 -18
  70. data/doc/thread_safety.rdoc +2 -4
  71. data/doc/transactions.rdoc +101 -19
  72. data/doc/validations.rdoc +64 -51
  73. data/doc/virtual_rows.rdoc +90 -109
  74. data/lib/sequel.rb +3 -1
  75. data/lib/sequel/adapters/ado.rb +154 -22
  76. data/lib/sequel/adapters/ado/access.rb +21 -21
  77. data/lib/sequel/adapters/ado/mssql.rb +8 -15
  78. data/lib/sequel/adapters/amalgalite.rb +17 -25
  79. data/lib/sequel/adapters/ibmdb.rb +52 -58
  80. data/lib/sequel/adapters/jdbc.rb +149 -127
  81. data/lib/sequel/adapters/jdbc/db2.rb +32 -40
  82. data/lib/sequel/adapters/jdbc/derby.rb +56 -58
  83. data/lib/sequel/adapters/jdbc/h2.rb +40 -30
  84. data/lib/sequel/adapters/jdbc/hsqldb.rb +22 -33
  85. data/lib/sequel/adapters/jdbc/jtds.rb +4 -10
  86. data/lib/sequel/adapters/jdbc/mssql.rb +6 -12
  87. data/lib/sequel/adapters/jdbc/mysql.rb +17 -18
  88. data/lib/sequel/adapters/jdbc/oracle.rb +25 -19
  89. data/lib/sequel/adapters/jdbc/postgresql.rb +90 -69
  90. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +14 -24
  91. data/lib/sequel/adapters/jdbc/sqlite.rb +50 -12
  92. data/lib/sequel/adapters/jdbc/sqlserver.rb +36 -9
  93. data/lib/sequel/adapters/jdbc/transactions.rb +25 -39
  94. data/lib/sequel/adapters/mock.rb +104 -113
  95. data/lib/sequel/adapters/mysql.rb +42 -61
  96. data/lib/sequel/adapters/mysql2.rb +126 -35
  97. data/lib/sequel/adapters/odbc.rb +21 -28
  98. data/lib/sequel/adapters/odbc/db2.rb +3 -1
  99. data/lib/sequel/adapters/odbc/mssql.rb +11 -15
  100. data/lib/sequel/adapters/odbc/oracle.rb +11 -0
  101. data/lib/sequel/adapters/oracle.rb +62 -68
  102. data/lib/sequel/adapters/postgres.rb +257 -311
  103. data/lib/sequel/adapters/postgresql.rb +3 -1
  104. data/lib/sequel/adapters/shared/access.rb +75 -79
  105. data/lib/sequel/adapters/shared/db2.rb +96 -74
  106. data/lib/sequel/adapters/shared/mssql.rb +258 -213
  107. data/lib/sequel/adapters/shared/mysql.rb +284 -216
  108. data/lib/sequel/adapters/shared/oracle.rb +175 -60
  109. data/lib/sequel/adapters/shared/postgres.rb +829 -383
  110. data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -127
  111. data/lib/sequel/adapters/shared/sqlite.rb +382 -159
  112. data/lib/sequel/adapters/sqlanywhere.rb +53 -38
  113. data/lib/sequel/adapters/sqlite.rb +111 -105
  114. data/lib/sequel/adapters/tinytds.rb +38 -46
  115. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -9
  116. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -5
  117. data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
  118. data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
  119. data/lib/sequel/adapters/utils/replace.rb +3 -4
  120. data/lib/sequel/adapters/utils/split_alter_table.rb +2 -0
  121. data/lib/sequel/adapters/utils/stored_procedures.rb +9 -22
  122. data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
  123. data/lib/sequel/ast_transformer.rb +13 -89
  124. data/lib/sequel/connection_pool.rb +54 -26
  125. data/lib/sequel/connection_pool/sharded_single.rb +19 -12
  126. data/lib/sequel/connection_pool/sharded_threaded.rb +160 -111
  127. data/lib/sequel/connection_pool/single.rb +21 -12
  128. data/lib/sequel/connection_pool/threaded.rb +137 -119
  129. data/lib/sequel/core.rb +352 -320
  130. data/lib/sequel/database.rb +19 -2
  131. data/lib/sequel/database/connecting.rb +70 -55
  132. data/lib/sequel/database/dataset.rb +15 -5
  133. data/lib/sequel/database/dataset_defaults.rb +20 -102
  134. data/lib/sequel/database/features.rb +20 -4
  135. data/lib/sequel/database/logging.rb +25 -7
  136. data/lib/sequel/database/misc.rb +132 -118
  137. data/lib/sequel/database/query.rb +51 -28
  138. data/lib/sequel/database/schema_generator.rb +188 -75
  139. data/lib/sequel/database/schema_methods.rb +161 -92
  140. data/lib/sequel/database/transactions.rb +260 -58
  141. data/lib/sequel/dataset.rb +28 -12
  142. data/lib/sequel/dataset/actions.rb +354 -170
  143. data/lib/sequel/dataset/dataset_module.rb +46 -0
  144. data/lib/sequel/dataset/features.rb +81 -34
  145. data/lib/sequel/dataset/graph.rb +82 -58
  146. data/lib/sequel/dataset/misc.rb +139 -47
  147. data/lib/sequel/dataset/placeholder_literalizer.rb +66 -26
  148. data/lib/sequel/dataset/prepared_statements.rb +188 -85
  149. data/lib/sequel/dataset/query.rb +428 -214
  150. data/lib/sequel/dataset/sql.rb +446 -339
  151. data/lib/sequel/deprecated.rb +14 -2
  152. data/lib/sequel/exceptions.rb +48 -16
  153. data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
  154. data/lib/sequel/extensions/_model_pg_row.rb +43 -0
  155. data/lib/sequel/extensions/_pretty_table.rb +10 -9
  156. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  157. data/lib/sequel/extensions/arbitrary_servers.rb +15 -11
  158. data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
  159. data/lib/sequel/extensions/blank.rb +2 -0
  160. data/lib/sequel/extensions/caller_logging.rb +79 -0
  161. data/lib/sequel/extensions/columns_introspection.rb +9 -4
  162. data/lib/sequel/extensions/connection_expiration.rb +99 -0
  163. data/lib/sequel/extensions/connection_validator.rb +26 -13
  164. data/lib/sequel/extensions/constant_sql_override.rb +65 -0
  165. data/lib/sequel/extensions/constraint_validations.rb +93 -38
  166. data/lib/sequel/extensions/core_extensions.rb +45 -53
  167. data/lib/sequel/extensions/core_refinements.rb +44 -46
  168. data/lib/sequel/extensions/current_datetime_timestamp.rb +5 -4
  169. data/lib/sequel/extensions/dataset_source_alias.rb +4 -0
  170. data/lib/sequel/extensions/date_arithmetic.rb +42 -16
  171. data/lib/sequel/extensions/datetime_parse_to_time.rb +37 -0
  172. data/lib/sequel/extensions/duplicate_columns_handler.rb +94 -0
  173. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -3
  174. data/lib/sequel/extensions/error_sql.rb +7 -3
  175. data/lib/sequel/extensions/escaped_like.rb +100 -0
  176. data/lib/sequel/extensions/eval_inspect.rb +14 -15
  177. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  178. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  179. data/lib/sequel/extensions/freeze_datasets.rb +3 -0
  180. data/lib/sequel/extensions/from_block.rb +2 -31
  181. data/lib/sequel/extensions/graph_each.rb +19 -6
  182. data/lib/sequel/extensions/identifier_mangling.rb +180 -0
  183. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  184. data/lib/sequel/extensions/index_caching.rb +109 -0
  185. data/lib/sequel/extensions/inflector.rb +8 -4
  186. data/lib/sequel/extensions/integer64.rb +32 -0
  187. data/lib/sequel/extensions/looser_typecasting.rb +19 -9
  188. data/lib/sequel/extensions/migration.rb +132 -80
  189. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +4 -0
  190. data/lib/sequel/extensions/named_timezones.rb +88 -23
  191. data/lib/sequel/extensions/no_auto_literal_strings.rb +4 -0
  192. data/lib/sequel/extensions/null_dataset.rb +12 -8
  193. data/lib/sequel/extensions/pagination.rb +35 -28
  194. data/lib/sequel/extensions/pg_array.rb +227 -316
  195. data/lib/sequel/extensions/pg_array_ops.rb +19 -7
  196. data/lib/sequel/extensions/pg_enum.rb +69 -24
  197. data/lib/sequel/extensions/pg_extended_date_support.rb +250 -0
  198. data/lib/sequel/extensions/pg_hstore.rb +50 -59
  199. data/lib/sequel/extensions/pg_hstore_ops.rb +9 -3
  200. data/lib/sequel/extensions/pg_inet.rb +34 -15
  201. data/lib/sequel/extensions/pg_inet_ops.rb +5 -1
  202. data/lib/sequel/extensions/pg_interval.rb +26 -26
  203. data/lib/sequel/extensions/pg_json.rb +422 -141
  204. data/lib/sequel/extensions/pg_json_ops.rb +248 -9
  205. data/lib/sequel/extensions/pg_loose_count.rb +5 -1
  206. data/lib/sequel/extensions/pg_range.rb +162 -146
  207. data/lib/sequel/extensions/pg_range_ops.rb +10 -5
  208. data/lib/sequel/extensions/pg_row.rb +53 -87
  209. data/lib/sequel/extensions/pg_row_ops.rb +36 -13
  210. data/lib/sequel/extensions/pg_static_cache_updater.rb +6 -2
  211. data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
  212. data/lib/sequel/extensions/pretty_table.rb +4 -0
  213. data/lib/sequel/extensions/query.rb +12 -7
  214. data/lib/sequel/extensions/round_timestamps.rb +6 -9
  215. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  216. data/lib/sequel/extensions/s.rb +59 -0
  217. data/lib/sequel/extensions/schema_caching.rb +14 -1
  218. data/lib/sequel/extensions/schema_dumper.rb +83 -55
  219. data/lib/sequel/extensions/select_remove.rb +8 -4
  220. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
  221. data/lib/sequel/extensions/server_block.rb +50 -17
  222. data/lib/sequel/extensions/server_logging.rb +61 -0
  223. data/lib/sequel/extensions/split_array_nil.rb +8 -4
  224. data/lib/sequel/extensions/sql_comments.rb +96 -0
  225. data/lib/sequel/extensions/sql_expr.rb +4 -1
  226. data/lib/sequel/extensions/string_agg.rb +181 -0
  227. data/lib/sequel/extensions/string_date_time.rb +2 -0
  228. data/lib/sequel/extensions/symbol_aref.rb +53 -0
  229. data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
  230. data/lib/sequel/extensions/symbol_as.rb +23 -0
  231. data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
  232. data/lib/sequel/extensions/synchronize_sql.rb +45 -0
  233. data/lib/sequel/extensions/thread_local_timezones.rb +4 -0
  234. data/lib/sequel/extensions/to_dot.rb +15 -5
  235. data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
  236. data/lib/sequel/model.rb +36 -126
  237. data/lib/sequel/model/associations.rb +850 -257
  238. data/lib/sequel/model/base.rb +652 -764
  239. data/lib/sequel/model/dataset_module.rb +13 -10
  240. data/lib/sequel/model/default_inflections.rb +3 -1
  241. data/lib/sequel/model/errors.rb +3 -3
  242. data/lib/sequel/model/exceptions.rb +12 -12
  243. data/lib/sequel/model/inflections.rb +8 -19
  244. data/lib/sequel/model/plugins.rb +111 -0
  245. data/lib/sequel/plugins/accessed_columns.rb +2 -0
  246. data/lib/sequel/plugins/active_model.rb +32 -7
  247. data/lib/sequel/plugins/after_initialize.rb +3 -1
  248. data/lib/sequel/plugins/association_dependencies.rb +27 -18
  249. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  250. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  251. data/lib/sequel/plugins/association_pks.rb +181 -83
  252. data/lib/sequel/plugins/association_proxies.rb +33 -9
  253. data/lib/sequel/plugins/auto_validations.rb +58 -23
  254. data/lib/sequel/plugins/before_after_save.rb +8 -0
  255. data/lib/sequel/plugins/blacklist_security.rb +23 -12
  256. data/lib/sequel/plugins/boolean_readers.rb +9 -6
  257. data/lib/sequel/plugins/boolean_subsets.rb +64 -0
  258. data/lib/sequel/plugins/caching.rb +27 -16
  259. data/lib/sequel/plugins/class_table_inheritance.rb +192 -94
  260. data/lib/sequel/plugins/column_conflicts.rb +18 -3
  261. data/lib/sequel/plugins/column_select.rb +9 -5
  262. data/lib/sequel/plugins/columns_updated.rb +42 -0
  263. data/lib/sequel/plugins/composition.rb +36 -24
  264. data/lib/sequel/plugins/constraint_validations.rb +37 -16
  265. data/lib/sequel/plugins/csv_serializer.rb +58 -35
  266. data/lib/sequel/plugins/dataset_associations.rb +60 -18
  267. data/lib/sequel/plugins/def_dataset_method.rb +90 -0
  268. data/lib/sequel/plugins/defaults_setter.rb +74 -13
  269. data/lib/sequel/plugins/delay_add_association.rb +4 -1
  270. data/lib/sequel/plugins/dirty.rb +65 -24
  271. data/lib/sequel/plugins/eager_each.rb +27 -3
  272. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  273. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  274. data/lib/sequel/plugins/error_splitter.rb +19 -12
  275. data/lib/sequel/plugins/finder.rb +246 -0
  276. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  277. data/lib/sequel/plugins/force_encoding.rb +9 -12
  278. data/lib/sequel/plugins/hook_class_methods.rb +39 -54
  279. data/lib/sequel/plugins/input_transformer.rb +20 -10
  280. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  281. data/lib/sequel/plugins/insert_returning_select.rb +4 -2
  282. data/lib/sequel/plugins/instance_filters.rb +12 -8
  283. data/lib/sequel/plugins/instance_hooks.rb +36 -17
  284. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  285. data/lib/sequel/plugins/inverted_subsets.rb +24 -13
  286. data/lib/sequel/plugins/json_serializer.rb +123 -47
  287. data/lib/sequel/plugins/lazy_attributes.rb +20 -14
  288. data/lib/sequel/plugins/list.rb +40 -26
  289. data/lib/sequel/plugins/many_through_many.rb +28 -12
  290. data/lib/sequel/plugins/modification_detection.rb +17 -5
  291. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -5
  292. data/lib/sequel/plugins/nested_attributes.rb +55 -28
  293. data/lib/sequel/plugins/optimistic_locking.rb +5 -3
  294. data/lib/sequel/plugins/pg_array_associations.rb +52 -18
  295. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +348 -0
  296. data/lib/sequel/plugins/pg_row.rb +7 -51
  297. data/lib/sequel/plugins/prepared_statements.rb +53 -72
  298. data/lib/sequel/plugins/prepared_statements_safe.rb +13 -5
  299. data/lib/sequel/plugins/rcte_tree.rb +43 -63
  300. data/lib/sequel/plugins/serialization.rb +37 -44
  301. data/lib/sequel/plugins/serialization_modification_detection.rb +3 -1
  302. data/lib/sequel/plugins/sharding.rb +17 -10
  303. data/lib/sequel/plugins/single_table_inheritance.rb +62 -28
  304. data/lib/sequel/plugins/singular_table_names.rb +2 -0
  305. data/lib/sequel/plugins/skip_create_refresh.rb +5 -3
  306. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  307. data/lib/sequel/plugins/split_values.rb +13 -6
  308. data/lib/sequel/plugins/static_cache.rb +79 -53
  309. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  310. data/lib/sequel/plugins/string_stripper.rb +5 -3
  311. data/lib/sequel/plugins/subclasses.rb +20 -2
  312. data/lib/sequel/plugins/subset_conditions.rb +48 -0
  313. data/lib/sequel/plugins/table_select.rb +4 -2
  314. data/lib/sequel/plugins/tactical_eager_loading.rb +120 -6
  315. data/lib/sequel/plugins/throw_failures.rb +110 -0
  316. data/lib/sequel/plugins/timestamps.rb +22 -8
  317. data/lib/sequel/plugins/touch.rb +21 -8
  318. data/lib/sequel/plugins/tree.rb +57 -30
  319. data/lib/sequel/plugins/typecast_on_load.rb +14 -4
  320. data/lib/sequel/plugins/unlimited_update.rb +3 -7
  321. data/lib/sequel/plugins/update_or_create.rb +6 -4
  322. data/lib/sequel/plugins/update_primary_key.rb +3 -1
  323. data/lib/sequel/plugins/update_refresh.rb +28 -15
  324. data/lib/sequel/plugins/uuid.rb +70 -0
  325. data/lib/sequel/plugins/validate_associated.rb +20 -0
  326. data/lib/sequel/plugins/validation_class_methods.rb +40 -19
  327. data/lib/sequel/plugins/validation_contexts.rb +49 -0
  328. data/lib/sequel/plugins/validation_helpers.rb +49 -31
  329. data/lib/sequel/plugins/whitelist_security.rb +122 -0
  330. data/lib/sequel/plugins/xml_serializer.rb +31 -30
  331. data/lib/sequel/sql.rb +479 -329
  332. data/lib/sequel/timezones.rb +62 -32
  333. data/lib/sequel/version.rb +10 -3
  334. metadata +177 -477
  335. data/Rakefile +0 -165
  336. data/doc/active_record.rdoc +0 -912
  337. data/doc/release_notes/1.0.txt +0 -38
  338. data/doc/release_notes/1.1.txt +0 -143
  339. data/doc/release_notes/1.3.txt +0 -101
  340. data/doc/release_notes/1.4.0.txt +0 -53
  341. data/doc/release_notes/1.5.0.txt +0 -155
  342. data/doc/release_notes/2.0.0.txt +0 -298
  343. data/doc/release_notes/2.1.0.txt +0 -271
  344. data/doc/release_notes/2.10.0.txt +0 -328
  345. data/doc/release_notes/2.11.0.txt +0 -215
  346. data/doc/release_notes/2.12.0.txt +0 -534
  347. data/doc/release_notes/2.2.0.txt +0 -253
  348. data/doc/release_notes/2.3.0.txt +0 -88
  349. data/doc/release_notes/2.4.0.txt +0 -106
  350. data/doc/release_notes/2.5.0.txt +0 -137
  351. data/doc/release_notes/2.6.0.txt +0 -157
  352. data/doc/release_notes/2.7.0.txt +0 -166
  353. data/doc/release_notes/2.8.0.txt +0 -171
  354. data/doc/release_notes/2.9.0.txt +0 -97
  355. data/doc/release_notes/3.0.0.txt +0 -221
  356. data/doc/release_notes/3.1.0.txt +0 -406
  357. data/doc/release_notes/3.10.0.txt +0 -286
  358. data/doc/release_notes/3.11.0.txt +0 -254
  359. data/doc/release_notes/3.12.0.txt +0 -304
  360. data/doc/release_notes/3.13.0.txt +0 -210
  361. data/doc/release_notes/3.14.0.txt +0 -118
  362. data/doc/release_notes/3.15.0.txt +0 -78
  363. data/doc/release_notes/3.16.0.txt +0 -45
  364. data/doc/release_notes/3.17.0.txt +0 -58
  365. data/doc/release_notes/3.18.0.txt +0 -120
  366. data/doc/release_notes/3.19.0.txt +0 -67
  367. data/doc/release_notes/3.2.0.txt +0 -268
  368. data/doc/release_notes/3.20.0.txt +0 -41
  369. data/doc/release_notes/3.21.0.txt +0 -87
  370. data/doc/release_notes/3.22.0.txt +0 -39
  371. data/doc/release_notes/3.23.0.txt +0 -172
  372. data/doc/release_notes/3.24.0.txt +0 -420
  373. data/doc/release_notes/3.25.0.txt +0 -88
  374. data/doc/release_notes/3.26.0.txt +0 -88
  375. data/doc/release_notes/3.27.0.txt +0 -82
  376. data/doc/release_notes/3.28.0.txt +0 -304
  377. data/doc/release_notes/3.29.0.txt +0 -459
  378. data/doc/release_notes/3.3.0.txt +0 -192
  379. data/doc/release_notes/3.30.0.txt +0 -135
  380. data/doc/release_notes/3.31.0.txt +0 -146
  381. data/doc/release_notes/3.32.0.txt +0 -202
  382. data/doc/release_notes/3.33.0.txt +0 -157
  383. data/doc/release_notes/3.34.0.txt +0 -671
  384. data/doc/release_notes/3.35.0.txt +0 -144
  385. data/doc/release_notes/3.36.0.txt +0 -245
  386. data/doc/release_notes/3.37.0.txt +0 -338
  387. data/doc/release_notes/3.38.0.txt +0 -234
  388. data/doc/release_notes/3.39.0.txt +0 -237
  389. data/doc/release_notes/3.4.0.txt +0 -325
  390. data/doc/release_notes/3.40.0.txt +0 -73
  391. data/doc/release_notes/3.41.0.txt +0 -155
  392. data/doc/release_notes/3.42.0.txt +0 -74
  393. data/doc/release_notes/3.43.0.txt +0 -105
  394. data/doc/release_notes/3.44.0.txt +0 -152
  395. data/doc/release_notes/3.45.0.txt +0 -179
  396. data/doc/release_notes/3.46.0.txt +0 -122
  397. data/doc/release_notes/3.47.0.txt +0 -270
  398. data/doc/release_notes/3.48.0.txt +0 -477
  399. data/doc/release_notes/3.5.0.txt +0 -510
  400. data/doc/release_notes/3.6.0.txt +0 -366
  401. data/doc/release_notes/3.7.0.txt +0 -179
  402. data/doc/release_notes/3.8.0.txt +0 -151
  403. data/doc/release_notes/3.9.0.txt +0 -233
  404. data/doc/release_notes/4.0.0.txt +0 -262
  405. data/doc/release_notes/4.1.0.txt +0 -85
  406. data/doc/release_notes/4.10.0.txt +0 -226
  407. data/doc/release_notes/4.11.0.txt +0 -147
  408. data/doc/release_notes/4.12.0.txt +0 -105
  409. data/doc/release_notes/4.13.0.txt +0 -169
  410. data/doc/release_notes/4.14.0.txt +0 -68
  411. data/doc/release_notes/4.15.0.txt +0 -56
  412. data/doc/release_notes/4.16.0.txt +0 -36
  413. data/doc/release_notes/4.17.0.txt +0 -38
  414. data/doc/release_notes/4.18.0.txt +0 -36
  415. data/doc/release_notes/4.19.0.txt +0 -45
  416. data/doc/release_notes/4.2.0.txt +0 -129
  417. data/doc/release_notes/4.20.0.txt +0 -79
  418. data/doc/release_notes/4.21.0.txt +0 -94
  419. data/doc/release_notes/4.22.0.txt +0 -72
  420. data/doc/release_notes/4.23.0.txt +0 -65
  421. data/doc/release_notes/4.24.0.txt +0 -99
  422. data/doc/release_notes/4.25.0.txt +0 -181
  423. data/doc/release_notes/4.26.0.txt +0 -44
  424. data/doc/release_notes/4.3.0.txt +0 -40
  425. data/doc/release_notes/4.4.0.txt +0 -92
  426. data/doc/release_notes/4.5.0.txt +0 -34
  427. data/doc/release_notes/4.6.0.txt +0 -30
  428. data/doc/release_notes/4.7.0.txt +0 -103
  429. data/doc/release_notes/4.8.0.txt +0 -175
  430. data/doc/release_notes/4.9.0.txt +0 -190
  431. data/lib/sequel/adapters/cubrid.rb +0 -142
  432. data/lib/sequel/adapters/do.rb +0 -156
  433. data/lib/sequel/adapters/do/mysql.rb +0 -64
  434. data/lib/sequel/adapters/do/postgres.rb +0 -42
  435. data/lib/sequel/adapters/do/sqlite3.rb +0 -40
  436. data/lib/sequel/adapters/jdbc/as400.rb +0 -82
  437. data/lib/sequel/adapters/jdbc/cubrid.rb +0 -62
  438. data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -34
  439. data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -31
  440. data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -31
  441. data/lib/sequel/adapters/odbc/progress.rb +0 -8
  442. data/lib/sequel/adapters/shared/cubrid.rb +0 -243
  443. data/lib/sequel/adapters/shared/firebird.rb +0 -245
  444. data/lib/sequel/adapters/shared/informix.rb +0 -52
  445. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -150
  446. data/lib/sequel/adapters/shared/progress.rb +0 -38
  447. data/lib/sequel/adapters/swift.rb +0 -158
  448. data/lib/sequel/adapters/swift/mysql.rb +0 -47
  449. data/lib/sequel/adapters/swift/postgres.rb +0 -45
  450. data/lib/sequel/adapters/swift/sqlite.rb +0 -47
  451. data/lib/sequel/adapters/utils/pg_types.rb +0 -68
  452. data/lib/sequel/dataset/mutation.rb +0 -109
  453. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -3
  454. data/lib/sequel/extensions/filter_having.rb +0 -59
  455. data/lib/sequel/extensions/hash_aliases.rb +0 -45
  456. data/lib/sequel/extensions/meta_def.rb +0 -31
  457. data/lib/sequel/extensions/query_literals.rb +0 -80
  458. data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -22
  459. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -118
  460. data/lib/sequel/extensions/set_overrides.rb +0 -72
  461. data/lib/sequel/no_core_ext.rb +0 -1
  462. data/lib/sequel/plugins/association_autoreloading.rb +0 -7
  463. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -7
  464. data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -78
  465. data/lib/sequel/plugins/prepared_statements_associations.rb +0 -117
  466. data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -59
  467. data/lib/sequel/plugins/schema.rb +0 -80
  468. data/lib/sequel/plugins/scissors.rb +0 -33
  469. data/spec/adapters/db2_spec.rb +0 -160
  470. data/spec/adapters/firebird_spec.rb +0 -411
  471. data/spec/adapters/informix_spec.rb +0 -100
  472. data/spec/adapters/mssql_spec.rb +0 -706
  473. data/spec/adapters/mysql_spec.rb +0 -1287
  474. data/spec/adapters/oracle_spec.rb +0 -313
  475. data/spec/adapters/postgres_spec.rb +0 -3725
  476. data/spec/adapters/spec_helper.rb +0 -43
  477. data/spec/adapters/sqlanywhere_spec.rb +0 -170
  478. data/spec/adapters/sqlite_spec.rb +0 -653
  479. data/spec/bin_spec.rb +0 -254
  480. data/spec/core/connection_pool_spec.rb +0 -1016
  481. data/spec/core/database_spec.rb +0 -2531
  482. data/spec/core/dataset_spec.rb +0 -5098
  483. data/spec/core/deprecated_spec.rb +0 -70
  484. data/spec/core/expression_filters_spec.rb +0 -1243
  485. data/spec/core/mock_adapter_spec.rb +0 -462
  486. data/spec/core/object_graph_spec.rb +0 -303
  487. data/spec/core/placeholder_literalizer_spec.rb +0 -163
  488. data/spec/core/schema_generator_spec.rb +0 -179
  489. data/spec/core/schema_spec.rb +0 -1659
  490. data/spec/core/spec_helper.rb +0 -34
  491. data/spec/core/version_spec.rb +0 -7
  492. data/spec/core_extensions_spec.rb +0 -699
  493. data/spec/extensions/accessed_columns_spec.rb +0 -51
  494. data/spec/extensions/active_model_spec.rb +0 -123
  495. data/spec/extensions/after_initialize_spec.rb +0 -24
  496. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  497. data/spec/extensions/association_dependencies_spec.rb +0 -117
  498. data/spec/extensions/association_pks_spec.rb +0 -365
  499. data/spec/extensions/association_proxies_spec.rb +0 -86
  500. data/spec/extensions/auto_validations_spec.rb +0 -192
  501. data/spec/extensions/blacklist_security_spec.rb +0 -88
  502. data/spec/extensions/blank_spec.rb +0 -69
  503. data/spec/extensions/boolean_readers_spec.rb +0 -93
  504. data/spec/extensions/caching_spec.rb +0 -270
  505. data/spec/extensions/class_table_inheritance_spec.rb +0 -420
  506. data/spec/extensions/column_conflicts_spec.rb +0 -60
  507. data/spec/extensions/column_select_spec.rb +0 -108
  508. data/spec/extensions/columns_introspection_spec.rb +0 -91
  509. data/spec/extensions/composition_spec.rb +0 -242
  510. data/spec/extensions/connection_validator_spec.rb +0 -120
  511. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -274
  512. data/spec/extensions/constraint_validations_spec.rb +0 -325
  513. data/spec/extensions/core_refinements_spec.rb +0 -519
  514. data/spec/extensions/csv_serializer_spec.rb +0 -173
  515. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  516. data/spec/extensions/dataset_associations_spec.rb +0 -311
  517. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  518. data/spec/extensions/date_arithmetic_spec.rb +0 -150
  519. data/spec/extensions/defaults_setter_spec.rb +0 -101
  520. data/spec/extensions/delay_add_association_spec.rb +0 -52
  521. data/spec/extensions/dirty_spec.rb +0 -180
  522. data/spec/extensions/eager_each_spec.rb +0 -42
  523. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  524. data/spec/extensions/error_splitter_spec.rb +0 -18
  525. data/spec/extensions/error_sql_spec.rb +0 -20
  526. data/spec/extensions/eval_inspect_spec.rb +0 -73
  527. data/spec/extensions/filter_having_spec.rb +0 -40
  528. data/spec/extensions/force_encoding_spec.rb +0 -114
  529. data/spec/extensions/from_block_spec.rb +0 -21
  530. data/spec/extensions/graph_each_spec.rb +0 -109
  531. data/spec/extensions/hash_aliases_spec.rb +0 -24
  532. data/spec/extensions/hook_class_methods_spec.rb +0 -429
  533. data/spec/extensions/inflector_spec.rb +0 -183
  534. data/spec/extensions/input_transformer_spec.rb +0 -54
  535. data/spec/extensions/insert_returning_select_spec.rb +0 -46
  536. data/spec/extensions/instance_filters_spec.rb +0 -79
  537. data/spec/extensions/instance_hooks_spec.rb +0 -276
  538. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  539. data/spec/extensions/json_serializer_spec.rb +0 -291
  540. data/spec/extensions/lazy_attributes_spec.rb +0 -170
  541. data/spec/extensions/list_spec.rb +0 -267
  542. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  543. data/spec/extensions/many_through_many_spec.rb +0 -2172
  544. data/spec/extensions/meta_def_spec.rb +0 -21
  545. data/spec/extensions/migration_spec.rb +0 -712
  546. data/spec/extensions/modification_detection_spec.rb +0 -80
  547. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
  548. data/spec/extensions/named_timezones_spec.rb +0 -108
  549. data/spec/extensions/nested_attributes_spec.rb +0 -697
  550. data/spec/extensions/null_dataset_spec.rb +0 -85
  551. data/spec/extensions/optimistic_locking_spec.rb +0 -128
  552. data/spec/extensions/pagination_spec.rb +0 -118
  553. data/spec/extensions/pg_array_associations_spec.rb +0 -736
  554. data/spec/extensions/pg_array_ops_spec.rb +0 -143
  555. data/spec/extensions/pg_array_spec.rb +0 -395
  556. data/spec/extensions/pg_enum_spec.rb +0 -92
  557. data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
  558. data/spec/extensions/pg_hstore_spec.rb +0 -206
  559. data/spec/extensions/pg_inet_ops_spec.rb +0 -101
  560. data/spec/extensions/pg_inet_spec.rb +0 -52
  561. data/spec/extensions/pg_interval_spec.rb +0 -76
  562. data/spec/extensions/pg_json_ops_spec.rb +0 -229
  563. data/spec/extensions/pg_json_spec.rb +0 -218
  564. data/spec/extensions/pg_loose_count_spec.rb +0 -17
  565. data/spec/extensions/pg_range_ops_spec.rb +0 -58
  566. data/spec/extensions/pg_range_spec.rb +0 -404
  567. data/spec/extensions/pg_row_ops_spec.rb +0 -60
  568. data/spec/extensions/pg_row_plugin_spec.rb +0 -62
  569. data/spec/extensions/pg_row_spec.rb +0 -360
  570. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
  571. data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
  572. data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
  573. data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
  574. data/spec/extensions/prepared_statements_spec.rb +0 -103
  575. data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
  576. data/spec/extensions/pretty_table_spec.rb +0 -92
  577. data/spec/extensions/query_literals_spec.rb +0 -183
  578. data/spec/extensions/query_spec.rb +0 -102
  579. data/spec/extensions/rcte_tree_spec.rb +0 -392
  580. data/spec/extensions/round_timestamps_spec.rb +0 -43
  581. data/spec/extensions/schema_caching_spec.rb +0 -41
  582. data/spec/extensions/schema_dumper_spec.rb +0 -789
  583. data/spec/extensions/schema_spec.rb +0 -117
  584. data/spec/extensions/scissors_spec.rb +0 -26
  585. data/spec/extensions/select_remove_spec.rb +0 -38
  586. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
  587. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  588. data/spec/extensions/serialization_spec.rb +0 -362
  589. data/spec/extensions/server_block_spec.rb +0 -90
  590. data/spec/extensions/set_overrides_spec.rb +0 -61
  591. data/spec/extensions/sharding_spec.rb +0 -198
  592. data/spec/extensions/shared_caching_spec.rb +0 -175
  593. data/spec/extensions/single_table_inheritance_spec.rb +0 -297
  594. data/spec/extensions/singular_table_names_spec.rb +0 -22
  595. data/spec/extensions/skip_create_refresh_spec.rb +0 -17
  596. data/spec/extensions/spec_helper.rb +0 -71
  597. data/spec/extensions/split_array_nil_spec.rb +0 -24
  598. data/spec/extensions/split_values_spec.rb +0 -22
  599. data/spec/extensions/sql_expr_spec.rb +0 -60
  600. data/spec/extensions/static_cache_spec.rb +0 -361
  601. data/spec/extensions/string_date_time_spec.rb +0 -95
  602. data/spec/extensions/string_stripper_spec.rb +0 -68
  603. data/spec/extensions/subclasses_spec.rb +0 -66
  604. data/spec/extensions/table_select_spec.rb +0 -71
  605. data/spec/extensions/tactical_eager_loading_spec.rb +0 -82
  606. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  607. data/spec/extensions/timestamps_spec.rb +0 -175
  608. data/spec/extensions/to_dot_spec.rb +0 -154
  609. data/spec/extensions/touch_spec.rb +0 -203
  610. data/spec/extensions/tree_spec.rb +0 -274
  611. data/spec/extensions/typecast_on_load_spec.rb +0 -80
  612. data/spec/extensions/unlimited_update_spec.rb +0 -20
  613. data/spec/extensions/update_or_create_spec.rb +0 -87
  614. data/spec/extensions/update_primary_key_spec.rb +0 -100
  615. data/spec/extensions/update_refresh_spec.rb +0 -53
  616. data/spec/extensions/validate_associated_spec.rb +0 -52
  617. data/spec/extensions/validation_class_methods_spec.rb +0 -1027
  618. data/spec/extensions/validation_helpers_spec.rb +0 -541
  619. data/spec/extensions/xml_serializer_spec.rb +0 -207
  620. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  621. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  622. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  623. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  624. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  625. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  626. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  627. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  628. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  629. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  630. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  631. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  632. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  633. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  634. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  635. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  636. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  637. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  638. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  639. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  640. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  641. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  642. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  643. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  644. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  645. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  646. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  647. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  648. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  649. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  650. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  651. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  652. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  653. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  654. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  655. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  656. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  657. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  658. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  659. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  660. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  661. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  662. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  663. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  664. data/spec/guards_helper.rb +0 -55
  665. data/spec/integration/associations_test.rb +0 -2454
  666. data/spec/integration/database_test.rb +0 -113
  667. data/spec/integration/dataset_test.rb +0 -1808
  668. data/spec/integration/eager_loader_test.rb +0 -687
  669. data/spec/integration/migrator_test.rb +0 -240
  670. data/spec/integration/model_test.rb +0 -226
  671. data/spec/integration/plugin_test.rb +0 -2240
  672. data/spec/integration/prepared_statement_test.rb +0 -467
  673. data/spec/integration/schema_test.rb +0 -817
  674. data/spec/integration/spec_helper.rb +0 -48
  675. data/spec/integration/timezone_test.rb +0 -86
  676. data/spec/integration/transaction_test.rb +0 -374
  677. data/spec/integration/type_test.rb +0 -133
  678. data/spec/model/association_reflection_spec.rb +0 -525
  679. data/spec/model/associations_spec.rb +0 -4426
  680. data/spec/model/base_spec.rb +0 -759
  681. data/spec/model/class_dataset_methods_spec.rb +0 -146
  682. data/spec/model/dataset_methods_spec.rb +0 -149
  683. data/spec/model/eager_loading_spec.rb +0 -2137
  684. data/spec/model/hooks_spec.rb +0 -604
  685. data/spec/model/inflector_spec.rb +0 -26
  686. data/spec/model/model_spec.rb +0 -982
  687. data/spec/model/plugins_spec.rb +0 -299
  688. data/spec/model/record_spec.rb +0 -2147
  689. data/spec/model/spec_helper.rb +0 -46
  690. data/spec/model/validations_spec.rb +0 -193
  691. data/spec/sequel_coverage.rb +0 -15
  692. data/spec/spec_config.rb +0 -10
@@ -1,13 +1,19 @@
1
- Sequel.require 'adapters/utils/emulate_offset_with_row_number'
1
+ # frozen-string-literal: true
2
+
3
+ require_relative '../utils/emulate_offset_with_row_number'
2
4
 
3
5
  module Sequel
4
6
  module Oracle
5
- module DatabaseMethods
6
- extend Sequel::Database::ResetIdentifierMangling
7
-
8
- TEMPORARY = 'GLOBAL TEMPORARY '.freeze
9
- AUTOINCREMENT = ''.freeze
7
+ Sequel::Database.set_shared_adapter_scheme(:oracle, self)
8
+
9
+ def self.mock_adapter_setup(db)
10
+ db.instance_exec do
11
+ @server_version = 11000000
12
+ @primary_key_sequences = {}
13
+ end
14
+ end
10
15
 
16
+ module DatabaseMethods
11
17
  attr_accessor :autosequence
12
18
 
13
19
  def create_sequence(name, opts=OPTS)
@@ -26,7 +32,6 @@ module Sequel
26
32
  self << drop_sequence_sql(name)
27
33
  end
28
34
 
29
- # Oracle uses the :oracle database type
30
35
  def database_type
31
36
  :oracle
32
37
  end
@@ -36,11 +41,22 @@ module Sequel
36
41
  im = input_identifier_meth
37
42
  schema, table = schema_and_table(table)
38
43
  ds = metadata_dataset.
39
- from(:all_cons_columns___pc, :all_constraints___p, :all_cons_columns___fc, :all_constraints___f).
40
- where(:f__table_name=>im.call(table), :f__constraint_type=>'R', :p__owner=>:f__r_owner, :p__constraint_name=>:f__r_constraint_name, :pc__owner=>:p__owner, :pc__constraint_name=>:p__constraint_name, :pc__table_name=>:p__table_name, :fc__owner=>:f__owner, :fc__constraint_name=>:f__constraint_name, :fc__table_name=>:f__table_name, :fc__position=>:pc__position).
41
- select(:p__table_name___table, :pc__column_name___key, :fc__column_name___column, :f__constraint_name___name).
42
- order(:table, :fc__position)
43
- ds = ds.where(:f__schema_name=>im.call(schema)) if schema
44
+ from{[all_cons_columns.as(:pc), all_constraints.as(:p), all_cons_columns.as(:fc), all_constraints.as(:f)]}.
45
+ where{{
46
+ f[:table_name]=>im.call(table),
47
+ f[:constraint_type]=>'R',
48
+ p[:owner]=>f[:r_owner],
49
+ p[:constraint_name]=>f[:r_constraint_name],
50
+ pc[:owner]=>p[:owner],
51
+ pc[:constraint_name]=>p[:constraint_name],
52
+ pc[:table_name]=>p[:table_name],
53
+ fc[:owner]=>f[:owner],
54
+ fc[:constraint_name]=>f[:constraint_name],
55
+ fc[:table_name]=>f[:table_name],
56
+ fc[:position]=>pc[:position]}}.
57
+ select{[p[:table_name].as(:table), pc[:column_name].as(:key), fc[:column_name].as(:column), f[:constraint_name].as(:name)]}.
58
+ order{[:table, fc[:position]]}
59
+ ds = ds.where{{f[:schema_name]=>im.call(schema)}} if schema
44
60
 
45
61
  fks = {}
46
62
  ds.each do |r|
@@ -54,12 +70,19 @@ module Sequel
54
70
  fks.values
55
71
  end
56
72
 
73
+ def freeze
74
+ current_user
75
+ server_version
76
+ @conversion_procs.freeze
77
+ super
78
+ end
79
+
57
80
  # Oracle namespaces indexes per table.
58
81
  def global_index_namespace?
59
82
  false
60
83
  end
61
84
 
62
- IGNORE_OWNERS = %w'APEX_040000 CTXSYS EXFSYS MDSYS OLAPSYS ORDDATA ORDSYS SYS SYSTEM XDB XDBMETADATA XDBPM XFILES WMSYS'
85
+ IGNORE_OWNERS = %w'APEX_040000 CTXSYS EXFSYS MDSYS OLAPSYS ORDDATA ORDSYS SYS SYSTEM XDB XDBMETADATA XDBPM XFILES WMSYS'.freeze
63
86
 
64
87
  def tables(opts=OPTS)
65
88
  m = output_identifier_meth
@@ -80,14 +103,37 @@ module Sequel
80
103
  map{|r| m.call(r[:view_name])}
81
104
  end
82
105
 
83
- def view_exists?(name)
84
- m = input_identifier_meth
85
- metadata_dataset.from(:all_views).
86
- exclude(:owner=>IGNORE_OWNERS).
87
- where(:view_name=>m.call(name)).
88
- count > 0
106
+ # Whether a view with a given name exists. By default, looks in all schemas other than system
107
+ # schemas. If the :current_schema option is given, looks in the schema for the current user.
108
+ def view_exists?(name, opts=OPTS)
109
+ ds = metadata_dataset.from(:all_views).where(:view_name=>input_identifier_meth.call(name))
110
+
111
+ if opts[:current_schema]
112
+ ds = ds.where(:owner=>Sequel.function(:SYS_CONTEXT, 'userenv', 'current_schema'))
113
+ else
114
+ ds = ds.exclude(:owner=>IGNORE_OWNERS)
115
+ end
116
+
117
+ ds.count > 0
89
118
  end
90
119
 
120
+ # The version of the Oracle server, used for determining capability.
121
+ def server_version(server=nil)
122
+ return @server_version if @server_version
123
+ @server_version = synchronize(server) do |conn|
124
+ (conn.server_version rescue nil) if conn.respond_to?(:server_version)
125
+ end
126
+ unless @server_version
127
+ @server_version = if m = /(\d+)\.(\d+)\.?(\d+)?\.?(\d+)?/.match(fetch("select version from PRODUCT_COMPONENT_VERSION where lower(product) like 'oracle%'").single_value)
128
+ (m[1].to_i*1000000) + (m[2].to_i*10000) + (m[3].to_i*100) + m[4].to_i
129
+ else
130
+ 0
131
+ end
132
+ end
133
+ @server_version
134
+ end
135
+
136
+
91
137
  # Oracle supports deferrable constraints.
92
138
  def supports_deferrable_constraints?
93
139
  true
@@ -100,7 +146,6 @@ module Sequel
100
146
 
101
147
  private
102
148
 
103
- # Handle Oracle specific ALTER TABLE SQL
104
149
  def alter_table_sql(table, op)
105
150
  case op[:op]
106
151
  when :add_column
@@ -130,7 +175,7 @@ module Sequel
130
175
  end
131
176
 
132
177
  def auto_increment_sql
133
- AUTOINCREMENT
178
+ ''
134
179
  end
135
180
 
136
181
  def create_sequence_sql(name, opts=OPTS)
@@ -186,6 +231,7 @@ module Sequel
186
231
  /check constraint .+ violated/ => CheckConstraintViolation,
187
232
  /cannot insert NULL into|cannot update .+ to NULL/ => NotNullConstraintViolation,
188
233
  /can't serialize access for this transaction/ => SerializationFailure,
234
+ /resource busy and acquire with NOWAIT specified or timeout/ => DatabaseLockTimeout,
189
235
  }.freeze
190
236
  def database_error_regexps
191
237
  DATABASE_ERROR_REGEXPS
@@ -200,14 +246,14 @@ module Sequel
200
246
  end
201
247
 
202
248
  def remove_cached_schema(table)
203
- @primary_key_sequences.delete(table)
249
+ Sequel.synchronize{@primary_key_sequences.delete(table)}
204
250
  super
205
251
  end
206
252
 
207
253
  TRANSACTION_ISOLATION_LEVELS = {:uncommitted=>'READ COMMITTED'.freeze,
208
254
  :committed=>'READ COMMITTED'.freeze,
209
255
  :repeatable=>'SERIALIZABLE'.freeze,
210
- :serializable=>'SERIALIZABLE'.freeze}
256
+ :serializable=>'SERIALIZABLE'.freeze}.freeze
211
257
  # Oracle doesn't support READ UNCOMMITTED OR REPEATABLE READ transaction
212
258
  # isolation levels, so upgrade to the next highest level in those cases.
213
259
  def set_transaction_isolation_sql(level)
@@ -216,13 +262,20 @@ module Sequel
216
262
 
217
263
  def sequence_for_table(table)
218
264
  return nil unless autosequence
219
- @primary_key_sequences.fetch(table) do |key|
220
- pk = schema(table).select{|k, v| v[:primary_key]}
221
- @primary_key_sequences[table] = if pk.length == 1
222
- seq = "seq_#{table}_#{pk.first.first}"
223
- seq.to_sym unless from(:user_sequences).filter(:sequence_name=>input_identifier_meth.call(seq)).empty?
224
- end
265
+ Sequel.synchronize{return @primary_key_sequences[table] if @primary_key_sequences.has_key?(table)}
266
+
267
+ begin
268
+ sch = schema(table)
269
+ rescue Sequel::Error
270
+ return nil
225
271
  end
272
+
273
+ pk = sch.select{|k, v| v[:primary_key]}
274
+ pks = if pk.length == 1
275
+ seq = "seq_#{table}_#{pk.first.first}"
276
+ seq.to_sym unless from(:user_sequences).where(:sequence_name=>input_identifier_meth.call(seq)).empty?
277
+ end
278
+ Sequel.synchronize{@primary_key_sequences[table] = pks}
226
279
  end
227
280
 
228
281
  # Oracle supports CREATE OR REPLACE VIEW.
@@ -233,13 +286,13 @@ module Sequel
233
286
  # Oracle's integer/:number type handles larger values than
234
287
  # most other databases's bigint types, so it should be
235
288
  # safe to use for Bignum.
236
- def type_literal_generic_bignum(column)
289
+ def type_literal_generic_bignum_symbol(column)
237
290
  :integer
238
291
  end
239
292
 
240
293
  # Oracle doesn't have a time type, so use timestamp for all
241
294
  # time columns.
242
- def type_literal_generic_time(column)
295
+ def type_literal_generic_only_time(column)
243
296
  :timestamp
244
297
  end
245
298
 
@@ -251,7 +304,7 @@ module Sequel
251
304
 
252
305
  # SQL fragment for showing a table is temporary
253
306
  def temporary_table_sql
254
- TEMPORARY
307
+ 'GLOBAL TEMPORARY '
255
308
  end
256
309
 
257
310
  # Oracle uses clob for text types.
@@ -267,21 +320,10 @@ module Sequel
267
320
 
268
321
  module DatasetMethods
269
322
  ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
270
- SPACE = Dataset::SPACE
271
- APOS = Dataset::APOS
272
- APOS_RE = Dataset::APOS_RE
273
- DOUBLE_APOS = Dataset::DOUBLE_APOS
274
- FROM = Dataset::FROM
275
- TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
276
- TIMESTAMP_OFFSET_FORMAT = "%+03i:%02i".freeze
277
- BOOL_FALSE = "'N'".freeze
278
- BOOL_TRUE = "'Y'".freeze
279
- HSTAR = "H*".freeze
280
- DUAL = ' FROM DUAL'.freeze
281
323
  BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
282
324
 
283
325
  include(Module.new do
284
- Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order lock')
326
+ Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order limit lock')
285
327
  end)
286
328
 
287
329
  def complex_expression_sql_append(sql, op, args)
@@ -296,6 +338,19 @@ module Sequel
296
338
  s2 = complex_expression_arg_pairs(x, &BITAND_PROC)
297
339
  Sequel.lit(["(", " - ", ")"], s1, s2)
298
340
  end
341
+ when :~, :'!~', :'~*', :'!~*'
342
+ raise InvalidOperation, "Pattern matching via regular expressions is not supported in this Oracle version" unless supports_regexp?
343
+ if op == :'!~' || op == :'!~*'
344
+ sql << 'NOT '
345
+ end
346
+ sql << 'REGEXP_LIKE('
347
+ literal_append(sql, args[0])
348
+ sql << ','
349
+ literal_append(sql, args[1])
350
+ if op == :'~*' || op == :'!~*'
351
+ sql << ", 'i'"
352
+ end
353
+ sql << ')'
299
354
  when :%, :<<, :>>, :'B~'
300
355
  complex_expression_emulate_append(sql, op, args)
301
356
  else
@@ -338,11 +393,15 @@ module Sequel
338
393
  clone(:sequence=>s)
339
394
  end
340
395
 
341
- # Handle LIMIT by using a unlimited subselect filtered with ROWNUM.
396
+ # Handle LIMIT by using a unlimited subselect filtered with ROWNUM,
397
+ # unless Oracle 12 is used.
342
398
  def select_sql
343
399
  return super if @opts[:sql]
344
- if o = @opts[:offset]
345
- columns = clone(:append_sql=>'', :placeholder_literal_null=>true).columns
400
+ return super if supports_fetch_next_rows?
401
+
402
+ o = @opts[:offset]
403
+ if o && o != 0
404
+ columns = clone(:append_sql=>String.new, :placeholder_literal_null=>true).columns
346
405
  dsa1 = dataset_alias(1)
347
406
  rn = row_number_column
348
407
  limit = @opts[:limit]
@@ -353,15 +412,15 @@ module Sequel
353
412
  select(*columns).
354
413
  where(SQL::Identifier.new(rn) > o)
355
414
  ds = ds.where(SQL::Identifier.new(rn) <= Sequel.+(o, limit)) if limit
356
- sql = @opts[:append_sql] || ''
415
+ sql = @opts[:append_sql] || String.new
357
416
  subselect_sql_append(sql, ds)
358
417
  sql
359
418
  elsif limit = @opts[:limit]
360
- ds = clone(:limit=>nil)
419
+ ds = unlimited
361
420
  # Lock doesn't work in subselects, so don't use a subselect when locking.
362
421
  # Don't use a subselect if custom SQL is used, as it breaks somethings.
363
422
  ds = ds.from_self unless @opts[:lock]
364
- sql = @opts[:append_sql] || ''
423
+ sql = @opts[:append_sql] || String.new
365
424
  subselect_sql_append(sql, ds.where(SQL::ComplexExpression.new(:<=, ROW_NUMBER_EXPRESSION, limit)))
366
425
  sql
367
426
  else
@@ -383,6 +442,12 @@ module Sequel
383
442
  false
384
443
  end
385
444
 
445
+ # Oracle supports FETCH NEXT ROWS since 12c, but it doesn't work when
446
+ # locking or when skipping locked rows.
447
+ def supports_fetch_next_rows?
448
+ server_version >= 12000000 && !(@opts[:lock] || @opts[:skip_locked])
449
+ end
450
+
386
451
  # Oracle supports GROUP BY CUBE
387
452
  def supports_group_cube?
388
453
  true
@@ -413,6 +478,11 @@ module Sequel
413
478
  false
414
479
  end
415
480
 
481
+ # Oracle supports NOWAIT.
482
+ def supports_nowait?
483
+ true
484
+ end
485
+
416
486
  # Oracle does not support offsets in correlated subqueries.
417
487
  def supports_offsets_in_correlated_subqueries?
418
488
  false
@@ -423,6 +493,11 @@ module Sequel
423
493
  false
424
494
  end
425
495
 
496
+ # Oracle supports SKIP LOCKED.
497
+ def supports_skip_locked?
498
+ true
499
+ end
500
+
426
501
  # Oracle supports timezones in literal timestamps.
427
502
  def supports_timestamp_timezones?
428
503
  true
@@ -438,6 +513,16 @@ module Sequel
438
513
  true
439
514
  end
440
515
 
516
+ # The version of the database server
517
+ def server_version
518
+ db.server_version(@opts[:server])
519
+ end
520
+
521
+ # Oracle 10+ supports pattern matching via regular expressions
522
+ def supports_regexp?
523
+ server_version >= 10010002
524
+ end
525
+
441
526
  private
442
527
 
443
528
  # Allow preparing prepared statements, since determining the prepared sql to use for
@@ -447,23 +532,24 @@ module Sequel
447
532
  end
448
533
 
449
534
  # Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
450
- # the use of AS anywhere, so this disables it in all cases.
535
+ # the use of AS anywhere, so this disables it in all cases. Oracle also does not support
536
+ # derived column lists in aliases.
451
537
  def as_sql_append(sql, aliaz, column_aliases=nil)
452
538
  raise Error, "oracle does not support derived column lists" if column_aliases
453
- sql << SPACE
539
+ sql << ' '
454
540
  quote_identifier_append(sql, aliaz)
455
541
  end
456
542
 
457
543
  # The strftime format to use when literalizing the time.
458
544
  def default_timestamp_format
459
- TIMESTAMP_FORMAT
545
+ "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'"
460
546
  end
461
547
 
462
548
  def empty_from_sql
463
- DUAL
549
+ ' FROM DUAL'
464
550
  end
465
551
 
466
- # There is no function on Microsoft SQL Server that does character length
552
+ # There is no function on Oracle that does character length
467
553
  # and respects trailing spaces (datalength respects trailing spaces, but
468
554
  # counts bytes instead of characters). Use a hack to work around the
469
555
  # trailing spaces issue.
@@ -495,7 +581,7 @@ module Sequel
495
581
 
496
582
  # Use a colon for the timestamp offset, since Oracle appears to require it.
497
583
  def format_timestamp_offset(hour, minute)
498
- sprintf(TIMESTAMP_OFFSET_FORMAT, hour, minute)
584
+ sprintf("%+03i:%02i", hour, minute)
499
585
  end
500
586
 
501
587
  # Oracle doesn't support empty values when inserting.
@@ -505,22 +591,22 @@ module Sequel
505
591
 
506
592
  # Use string in hex format for blob data.
507
593
  def literal_blob_append(sql, v)
508
- sql << APOS << v.unpack(HSTAR).first << APOS
594
+ sql << "'" << v.unpack("H*").first << "'"
509
595
  end
510
596
 
511
597
  # Oracle uses 'N' for false values.
512
598
  def literal_false
513
- BOOL_FALSE
599
+ "'N'"
514
600
  end
515
601
 
516
602
  # Oracle uses the SQL standard of only doubling ' inside strings.
517
603
  def literal_string_append(sql, v)
518
- sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
604
+ sql << "'" << v.gsub("'", "''") << "'"
519
605
  end
520
606
 
521
607
  # Oracle uses 'Y' for true values.
522
608
  def literal_true
523
- BOOL_TRUE
609
+ "'Y'"
524
610
  end
525
611
 
526
612
  # Oracle can insert multiple rows using a UNION
@@ -528,6 +614,35 @@ module Sequel
528
614
  :union
529
615
  end
530
616
 
617
+ def select_limit_sql(sql)
618
+ return unless supports_fetch_next_rows?
619
+
620
+ if offset = @opts[:offset]
621
+ sql << " OFFSET "
622
+ literal_append(sql, offset)
623
+ sql << " ROWS"
624
+ end
625
+
626
+ if limit = @opts[:limit]
627
+ sql << " FETCH NEXT "
628
+ literal_append(sql, limit)
629
+ sql << " ROWS ONLY"
630
+ end
631
+ end
632
+
633
+ # Use SKIP LOCKED if skipping locked rows.
634
+ def select_lock_sql(sql)
635
+ super
636
+
637
+ if @opts[:lock]
638
+ if @opts[:skip_locked]
639
+ sql << " SKIP LOCKED"
640
+ elsif @opts[:nowait]
641
+ sql << " NOWAIT"
642
+ end
643
+ end
644
+ end
645
+
531
646
  # Oracle supports quoted function names.
532
647
  def supports_quoted_function_names?
533
648
  true
@@ -1,61 +1,113 @@
1
- Sequel.require 'adapters/utils/pg_types'
1
+ # frozen-string-literal: true
2
+
3
+ require_relative '../utils/unmodified_identifiers'
2
4
 
3
5
  module Sequel
4
6
  # Top level module for holding all PostgreSQL-related modules and classes
5
- # for Sequel. There are a few module level accessors that are added via
6
- # metaprogramming. These are:
7
- #
8
- # client_min_messages :: Change the minimum level of messages that PostgreSQL will send to the
9
- # the client. The PostgreSQL default is NOTICE, the Sequel default is
10
- # WARNING. Set to nil to not change the server default. Overridable on
11
- # a per instance basis via the :client_min_messages option.
12
- # force_standard_strings :: Set to false to not force the use of standard strings. Overridable
13
- # on a per instance basis via the :force_standard_strings option.
14
- #
15
- # It is not recommened you use these module-level accessors. Instead,
16
- # use the database option to make the setting per-Database.
17
- #
18
- # All adapters that connect to PostgreSQL support the following option in
19
- # addition to those mentioned above:
7
+ # for Sequel. All adapters that connect to PostgreSQL support the following options:
20
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.
21
15
  # :search_path :: Set the schema search_path for this Database's connections.
22
16
  # Allows to to set which schemas do not need explicit
23
17
  # qualification, and in which order to check the schemas when
24
18
  # an unqualified object is referenced.
25
19
  module Postgres
26
- # Array of exceptions that need to be converted. JDBC
27
- # uses NativeExceptions, the native adapter uses PGError.
28
- CONVERTED_EXCEPTIONS = []
29
-
30
- @client_min_messages = :warning
31
- @force_standard_strings = true
32
-
33
- class << self
34
- # By default, Sequel sets the minimum level of log messages sent to the client
35
- # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
36
- # of mostly useless messages when running migrations, such as a couple of lines
37
- # for every serial primary key field.
38
- attr_accessor :client_min_messages
39
-
40
- # By default, Sequel forces the use of standard strings, so that
41
- # '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
42
- # to interpreting plain strings, newer versions use standard strings by
43
- # default. Sequel assumes that SQL standard strings will be used. Setting
44
- # this to false means Sequel will use the database's default.
45
- 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
86
+ end
87
+
88
+ def self.mock_adapter_setup(db)
89
+ db.instance_exec do
90
+ @server_version = 90500
91
+ initialize_postgres_adapter
92
+ extend(MockAdapterDatabaseMethods)
93
+ end
46
94
  end
47
95
 
48
- class CreateTableGenerator < Sequel::Schema::Generator
96
+ class CreateTableGenerator < Sequel::Schema::CreateTableGenerator
49
97
  # Add an exclusion constraint when creating the table. Elements should be
50
98
  # an array of 2 element arrays, with the first element being the column or
51
99
  # expression the exclusion constraint is applied to, and the second element
52
- # being the operator to use for the column/expression to check for exclusion.
53
- #
54
- # Example:
100
+ # being the operator to use for the column/expression to check for exclusion:
55
101
  #
56
102
  # exclude([[:col1, '&&'], [:col2, '=']])
57
103
  # # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
58
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
+ #
59
111
  # Options supported:
60
112
  #
61
113
  # :name :: Name the constraint with the given name (useful if you may
@@ -82,21 +134,106 @@ module Sequel
82
134
  end
83
135
  end
84
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
+
85
227
  # Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
86
228
  class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
87
229
 
88
- # Methods shared by Database instances that connect to PostgreSQL.
89
230
  module DatabaseMethods
90
- extend Sequel::Database::ResetIdentifierMangling
231
+ include UnmodifiedIdentifiers::DatabaseMethods
91
232
 
92
233
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
93
- RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
94
- 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
95
- POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
96
- UNLOGGED = 'UNLOGGED '.freeze
97
- ON_COMMIT = {
98
- :drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS',
99
- }.freeze
234
+ FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'=>:no_action, 'r'=>:restrict, 'c'=>:cascade, 'n'=>:set_null, 'd'=>:set_default}.freeze
235
+ ON_COMMIT = {:drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS'}.freeze
236
+ ON_COMMIT.each_value(&:freeze)
100
237
 
101
238
  # SQL fragment for custom sequences (ones not created by serial primary key),
102
239
  # Returning the schema and literal form of the sequence name, by parsing
@@ -104,10 +241,10 @@ module Sequel
104
241
  SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
105
242
  SELECT name.nspname AS "schema",
106
243
  CASE
107
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
108
- substr(split_part(def.adsrc, '''', 2),
109
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
110
- ELSE split_part(def.adsrc, '''', 2)
244
+ WHEN split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2) ~ '.' THEN
245
+ substr(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2),
246
+ strpos(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2), '.')+1)
247
+ ELSE split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2)
111
248
  END AS "sequence"
112
249
  FROM pg_class t
113
250
  JOIN pg_namespace name ON (t.relnamespace = name.oid)
@@ -115,7 +252,7 @@ module Sequel
115
252
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
116
253
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
117
254
  WHERE cons.contype = 'p'
118
- AND def.adsrc ~* 'nextval'
255
+ AND pg_get_expr(def.adbin, attr.attrelid) ~* 'nextval'
119
256
  end_sql
120
257
  ).strip.gsub(/\s+/, ' ').freeze
121
258
 
@@ -154,21 +291,120 @@ module Sequel
154
291
  # having callable values for the conversion proc for that type.
155
292
  attr_reader :conversion_procs
156
293
 
157
- # Add a conversion proc for a named type. This should be used
158
- # for types without fixed OIDs, which includes all types that
159
- # are not included in a default PostgreSQL installation. If
160
- # a block is given, it is used as the conversion proc, otherwise
161
- # the conversion proc is looked up in the PG_NAMED_TYPES hash.
294
+ # Set a conversion proc for the given oid. The callable can
295
+ # be passed either as a argument or a block.
296
+ def add_conversion_proc(oid, callable=nil, &block)
297
+ conversion_procs[oid] = callable || block
298
+ end
299
+
300
+ # Add a conversion proc for a named type, using the given block.
301
+ # This should be used for types without fixed OIDs, which includes all types that
302
+ # are not included in a default PostgreSQL installation.
162
303
  def add_named_conversion_proc(name, &block)
163
- add_named_conversion_procs(conversion_procs, name=>(block || PG_NAMED_TYPES[name]))
304
+ unless oid = from(:pg_type).where(:typtype=>['b', 'e'], :typname=>name.to_s).get(:oid)
305
+ raise Error, "No matching type in pg_type for #{name.inspect}"
306
+ end
307
+ add_conversion_proc(oid, block)
164
308
  end
165
309
 
166
- # Commit an existing prepared transaction with the given transaction
167
- # identifier string.
168
310
  def commit_prepared_transaction(transaction_id, opts=OPTS)
169
311
  run("COMMIT PREPARED #{literal(transaction_id)}", opts)
170
312
  end
171
313
 
314
+ # A hash of metadata for CHECK constraints on the table.
315
+ # Keys are CHECK constraint name symbols. Values are hashes with the following keys:
316
+ # :definition :: An SQL fragment for the definition of the constraint
317
+ # :columns :: An array of column symbols for the columns referenced in the constraint,
318
+ # can be an empty array if the database cannot deteremine the column symbols.
319
+ def check_constraints(table)
320
+ m = output_identifier_meth
321
+
322
+ rows = metadata_dataset.
323
+ from{pg_constraint.as(:co)}.
324
+ left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
325
+ where(:conrelid=>regclass_oid(table), :contype=>'c').
326
+ select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
327
+
328
+ hash = {}
329
+ rows.each do |row|
330
+ constraint = m.call(row[:constraint])
331
+ entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
332
+ entry[:columns] << m.call(row[:column]) if row[:column]
333
+ end
334
+
335
+ hash
336
+ end
337
+
338
+ # Convert the first primary key column in the +table+ from being a serial column to being an identity column.
339
+ # If the column is already an identity column, assume it was already converted and make no changes.
340
+ #
341
+ # Only supported on PostgreSQL 10.2+, since on those versions Sequel will use identity columns
342
+ # instead of serial columns for auto incrementing primary keys. Only supported when running as
343
+ # a superuser, since regular users cannot modify system tables, and there is no way to keep an
344
+ # existing sequence when changing an existing column to be an identity column.
345
+ #
346
+ # This method can raise an exception in at least the following cases where it may otherwise succeed
347
+ # (there may be additional cases not listed here):
348
+ #
349
+ # * The serial column was added after table creation using PostgreSQL <7.3
350
+ # * A regular index also exists on the column (such an index can probably be dropped as the
351
+ # primary key index should suffice)
352
+ #
353
+ # Options:
354
+ # :column :: Specify the column to convert instead of using the first primary key column
355
+ # :server :: Run the SQL on the given server
356
+ def convert_serial_to_identity(table, opts=OPTS)
357
+ raise Error, "convert_serial_to_identity is only supported on PostgreSQL 10.2+" unless server_version >= 100002
358
+
359
+ server = opts[:server]
360
+ server_hash = server ? {:server=>server} : OPTS
361
+ ds = dataset
362
+ ds = ds.server(server) if server
363
+
364
+ raise Error, "convert_serial_to_identity requires superuser permissions" unless ds.get{current_setting('is_superuser')} == 'on'
365
+
366
+ table_oid = regclass_oid(table)
367
+ im = input_identifier_meth
368
+ unless column = im.call(opts[:column] || ((sch = schema(table).find{|_, sc| sc[:primary_key] && sc[:auto_increment]}) && sch[0]))
369
+ raise Error, "could not determine column to convert from serial to identity automatically"
370
+ end
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
+
172
408
  # Creates the function in the database. Arguments:
173
409
  # name :: name of the function to create
174
410
  # definition :: string definition of the function, or object file for a dynamically loaded C function.
@@ -213,6 +449,26 @@ module Sequel
213
449
  self << create_schema_sql(name, opts)
214
450
  end
215
451
 
452
+ # Support partitions of tables using the :partition_of option.
453
+ def create_table(name, options=OPTS, &block)
454
+ if options[:partition_of]
455
+ create_partition_of_table_from_generator(name, CreatePartitionOfTableGenerator.new(&block), options)
456
+ return
457
+ end
458
+
459
+ super
460
+ end
461
+
462
+ # Support partitions of tables using the :partition_of option.
463
+ def create_table?(name, options=OPTS, &block)
464
+ if options[:partition_of]
465
+ create_table(name, options.merge!(:if_not_exists=>true), &block)
466
+ return
467
+ end
468
+
469
+ super
470
+ end
471
+
216
472
  # Create a trigger in the database. Arguments:
217
473
  # table :: the table on which this trigger operates
218
474
  # name :: the name of this trigger
@@ -228,7 +484,6 @@ module Sequel
228
484
  self << create_trigger_sql(table, name, function, opts)
229
485
  end
230
486
 
231
- # PostgreSQL uses the :postgres database type.
232
487
  def database_type
233
488
  :postgres
234
489
  end
@@ -283,75 +538,144 @@ module Sequel
283
538
 
284
539
  # Return full foreign key information using the pg system tables, including
285
540
  # :name, :on_delete, :on_update, and :deferrable entries in the hashes.
541
+ #
542
+ # Supports additional options:
543
+ # :reverse :: Instead of returning foreign keys in the current table, return
544
+ # foreign keys in other tables that reference the current table.
545
+ # :schema :: Set to true to have the :table value in the hashes be a qualified
546
+ # identifier. Set to false to use a separate :schema value with
547
+ # the related schema. Defaults to whether the given table argument
548
+ # is a qualified identifier.
286
549
  def foreign_key_list(table, opts=OPTS)
287
550
  m = output_identifier_meth
288
551
  schema, _ = opts.fetch(:schema, schema_and_table(table))
289
- range = 0...32
290
-
291
- base_ds = metadata_dataset.
292
- from(:pg_constraint___co).
293
- join(:pg_class___cl, :oid=>:conrelid).
294
- where(:cl__relkind=>'r', :co__contype=>'f', :cl__oid=>regclass_oid(table))
295
-
296
- # We split the parsing into two separate queries, which are merged manually later.
297
- # This is because PostgreSQL stores both the referencing and referenced columns in
298
- # arrays, and I don't know a simple way to not create a cross product, as PostgreSQL
299
- # doesn't appear to have a function that takes an array and element and gives you
300
- # the index of that element in the array.
301
-
302
- ds = base_ds.
303
- join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
304
- order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
305
- select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
306
- SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
307
-
308
- ref_ds = base_ds.
309
- join(:pg_class___cl2, :oid=>:co__confrelid).
310
- join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
311
- order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att2__attnum)).
312
- select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
313
-
314
- # If a schema is given, we only search in that schema, and the returned :table
315
- # entry is schema qualified as well.
316
- if schema
317
- ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
318
- select_more(:nsp2__nspname___schema)
552
+ oid = regclass_oid(table)
553
+ reverse = opts[:reverse]
554
+
555
+ if reverse
556
+ ctable = Sequel[:att2]
557
+ cclass = Sequel[:cl2]
558
+ rtable = Sequel[:att]
559
+ rclass = Sequel[:cl]
560
+ else
561
+ ctable = Sequel[:att]
562
+ cclass = Sequel[:cl]
563
+ rtable = Sequel[:att2]
564
+ rclass = Sequel[:cl2]
565
+ end
566
+
567
+ if server_version >= 90500
568
+ cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
569
+ rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
570
+ else
571
+ range = 0...32
572
+ cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
573
+ rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
574
+ end
575
+
576
+ ds = metadata_dataset.
577
+ from{pg_constraint.as(:co)}.
578
+ join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
579
+ join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
580
+ join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
581
+ join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
582
+ join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
583
+ order{[co[:conname], cpos]}.
584
+ where{{
585
+ cl[:relkind]=>'r',
586
+ co[:contype]=>'f',
587
+ cl[:oid]=>oid,
588
+ cpos=>rpos
589
+ }}.
590
+ select{[
591
+ co[:conname].as(:name),
592
+ ctable[:attname].as(:column),
593
+ co[:confupdtype].as(:on_update),
594
+ co[:confdeltype].as(:on_delete),
595
+ cl2[:relname].as(:table),
596
+ rtable[:attname].as(:refcolumn),
597
+ SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
598
+ nsp[:nspname].as(:schema)
599
+ ]}
600
+
601
+ if reverse
602
+ ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
319
603
  end
320
604
 
321
605
  h = {}
322
606
  fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
607
+
323
608
  ds.each do |row|
324
- if r = h[row[:name]]
609
+ if reverse
610
+ key = [row[:schema], row[:table], row[:name]]
611
+ else
612
+ key = row[:name]
613
+ end
614
+
615
+ if r = h[key]
325
616
  r[:columns] << m.call(row[:column])
617
+ r[:key] << m.call(row[:refcolumn])
326
618
  else
327
- 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]}
619
+ entry = h[key] = {
620
+ :name=>m.call(row[:name]),
621
+ :columns=>[m.call(row[:column])],
622
+ :key=>[m.call(row[:refcolumn])],
623
+ :on_update=>fklod_map[row[:on_update]],
624
+ :on_delete=>fklod_map[row[:on_delete]],
625
+ :deferrable=>row[:deferrable],
626
+ :table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
627
+ }
628
+
629
+ unless schema
630
+ # If not combining schema information into the :table entry
631
+ # include it as a separate entry.
632
+ entry[:schema] = m.call(row[:schema])
633
+ end
328
634
  end
329
635
  end
330
- ref_ds.each do |row|
331
- r = h[row[:name]]
332
- r[:table] ||= schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
333
- r[:key] ||= []
334
- r[:key] << m.call(row[:refcolumn])
335
- end
636
+
336
637
  h.values
337
638
  end
338
639
 
640
+ def freeze
641
+ server_version
642
+ supports_prepared_transactions?
643
+ @conversion_procs.freeze
644
+ super
645
+ end
646
+
339
647
  # Use the pg_* system tables to determine indexes on a table
340
648
  def indexes(table, opts=OPTS)
341
649
  m = output_identifier_meth
342
- range = 0...32
343
- attnums = server_version >= 80100 ? SQL::Function.new(:ANY, :ind__indkey) : range.map{|x| SQL::Subscript.new(:ind__indkey, [x])}
344
- ds = metadata_dataset.
345
- from(:pg_class___tab).
346
- join(:pg_index___ind, :indrelid=>:oid).
347
- join(:pg_class___indc, :oid=>:indexrelid).
348
- join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
349
- left_join(:pg_constraint___con, :conname=>:indc__relname).
350
- filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true, :tab__oid=>regclass_oid(table, opts)).
351
- order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
352
- select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column, :con__condeferrable___deferrable)
650
+ oid = regclass_oid(table, opts)
651
+
652
+ if server_version >= 90500
653
+ order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
654
+ else
655
+ range = 0...32
656
+ order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
657
+ end
658
+
659
+ attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
353
660
 
354
- ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
661
+ ds = metadata_dataset.
662
+ from{pg_class.as(:tab)}.
663
+ join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
664
+ join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
665
+ join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
666
+ left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
667
+ where{{
668
+ indc[:relkind]=>'i',
669
+ ind[:indisprimary]=>false,
670
+ :indexprs=>nil,
671
+ :indisvalid=>true,
672
+ tab[:oid]=>oid}}.
673
+ order(*order).
674
+ select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
675
+
676
+ ds = ds.where(:indpred=>nil) unless opts[:include_partial]
677
+ ds = ds.where(:indisready=>true) if server_version >= 80300
678
+ ds = ds.where(:indislive=>true) if server_version >= 90300
355
679
 
356
680
  indexes = {}
357
681
  ds.each do |r|
@@ -363,7 +687,7 @@ module Sequel
363
687
 
364
688
  # Dataset containing all current database locks
365
689
  def locks
366
- dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
690
+ dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select{[pg_class[:relname], Sequel::SQL::ColumnAll.new(:pg_locks)]}
367
691
  end
368
692
 
369
693
  # Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
@@ -373,7 +697,8 @@ module Sequel
373
697
  # :server :: The server to which to send the NOTIFY statement, if the sharding support
374
698
  # is being used.
375
699
  def notify(channel, opts=OPTS)
376
- sql = "NOTIFY "
700
+ sql = String.new
701
+ sql << "NOTIFY "
377
702
  dataset.send(:identifier_append, sql, channel)
378
703
  if payload = opts[:payload]
379
704
  sql << ", "
@@ -418,28 +743,28 @@ module Sequel
418
743
  run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
419
744
  end
420
745
 
421
- # Reset the database's conversion procs, requires a server query if there
422
- # any named types.
423
- def reset_conversion_procs
424
- @conversion_procs = get_conversion_procs
425
- conversion_procs_updated
426
- @conversion_procs
427
- end
428
-
429
746
  # Reset the primary key sequence for the given table, basing it on the
430
747
  # maximum current value of the table's primary key.
431
748
  def reset_primary_key_sequence(table)
432
749
  return unless seq = primary_key_sequence(table)
433
750
  pk = SQL::Identifier.new(primary_key(table))
434
751
  db = self
435
- seq_ds = db.from(LiteralString.new(seq))
436
752
  s, t = schema_and_table(table)
437
753
  table = Sequel.qualify(s, t) if s
438
- get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
754
+
755
+ if server_version >= 100000
756
+ seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
757
+ increment_by = :seqincrement
758
+ min_value = :seqmin
759
+ else
760
+ seq_ds = metadata_dataset.from(LiteralString.new(seq))
761
+ increment_by = :increment_by
762
+ min_value = :min_value
763
+ end
764
+
765
+ get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
439
766
  end
440
767
 
441
- # Rollback an existing prepared transaction with the given transaction
442
- # identifier string.
443
768
  def rollback_prepared_transaction(transaction_id, opts=OPTS)
444
769
  run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
445
770
  end
@@ -447,24 +772,16 @@ module Sequel
447
772
  # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
448
773
  # managing incrementing primary keys.
449
774
  def serial_primary_key_options
450
- {:primary_key => true, :serial => true, :type=>Integer}
775
+ auto_increment_key = server_version >= 100002 ? :identity : :serial
776
+ {:primary_key => true, auto_increment_key => true, :type=>Integer}
451
777
  end
452
778
 
453
779
  # The version of the PostgreSQL server, used for determining capability.
454
780
  def server_version(server=nil)
455
781
  return @server_version if @server_version
456
- @server_version = synchronize(server) do |conn|
457
- (conn.server_version rescue nil) if conn.respond_to?(:server_version)
458
- end
459
- unless @server_version
460
- @server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
461
- (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
462
- else
463
- 0
464
- end
465
- end
466
- warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
467
- @server_version
782
+ ds = dataset
783
+ ds = ds.server(server) if server
784
+ @server_version ||= ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value rescue 0
468
785
  end
469
786
 
470
787
  # PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
@@ -535,17 +852,18 @@ module Sequel
535
852
  # Check whether the given type name string/symbol (e.g. :hstore) is supported by
536
853
  # the database.
537
854
  def type_supported?(type)
538
- @supported_types ||= {}
539
- @supported_types.fetch(type){@supported_types[type] = (from(:pg_type).filter(:typtype=>'b', :typname=>type.to_s).count > 0)}
855
+ Sequel.synchronize{return @supported_types[type] if @supported_types.has_key?(type)}
856
+ supported = from(:pg_type).where(:typtype=>'b', :typname=>type.to_s).count > 0
857
+ Sequel.synchronize{return @supported_types[type] = supported}
540
858
  end
541
859
 
542
860
  # Creates a dataset that uses the VALUES clause:
543
861
  #
544
862
  # DB.values([[1, 2], [3, 4]])
545
- # VALUES ((1, 2), (3, 4))
863
+ # # VALUES ((1, 2), (3, 4))
546
864
  #
547
865
  # DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
548
- # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
866
+ # # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
549
867
  def values(v)
550
868
  @default_dataset.clone(:values=>v)
551
869
  end
@@ -553,28 +871,22 @@ module Sequel
553
871
  # Array of symbols specifying view names in the current database.
554
872
  #
555
873
  # Options:
874
+ # :materialized :: Return materialized views
556
875
  # :qualify :: Return the views as Sequel::SQL::QualifiedIdentifier instances,
557
876
  # using the schema the view is located in as the qualifier.
558
877
  # :schema :: The schema to search
559
878
  # :server :: The server to use
560
879
  def views(opts=OPTS)
561
- pg_class_relname('v', opts)
880
+ relkind = opts[:materialized] ? 'm' : 'v'
881
+ pg_class_relname(relkind, opts)
562
882
  end
563
883
 
564
884
  private
565
885
 
566
- # Do a type name-to-oid lookup using the database and update the procs
567
- # with the related proc if the database supports the type.
568
- def add_named_conversion_procs(procs, named_procs)
569
- unless (named_procs).empty?
570
- convert_named_procs_to_procs(named_procs).each do |oid, pr|
571
- procs[oid] ||= pr
572
- end
573
- conversion_procs_updated
574
- end
886
+ def alter_table_add_column_sql(table, op)
887
+ "ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
575
888
  end
576
889
 
577
- # Use a PostgreSQL-specific alter table generator
578
890
  def alter_table_generator_class
579
891
  Postgres::AlterTableGenerator
580
892
  end
@@ -583,7 +895,7 @@ module Sequel
583
895
  s = super
584
896
  if using = op[:using]
585
897
  using = Sequel::LiteralString.new(using) if using.is_a?(String)
586
- s << ' USING '
898
+ s += ' USING '
587
899
  s << literal(using)
588
900
  end
589
901
  s
@@ -634,9 +946,24 @@ module Sequel
634
946
  end
635
947
  end
636
948
 
949
+ # Support identity columns, but only use the identity SQL syntax if no
950
+ # default value is given.
951
+ def column_definition_default_sql(sql, column)
952
+ super
953
+ if !column[:serial] && !['smallserial', 'serial', 'bigserial'].include?(column[:type].to_s) && !column[:default]
954
+ if (identity = column[:identity])
955
+ sql << " GENERATED "
956
+ sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
957
+ sql << " AS IDENTITY"
958
+ elsif (generated = column[:generated_always_as])
959
+ sql << " GENERATED ALWAYS AS (#{literal(generated)}) STORED"
960
+ end
961
+ end
962
+ end
963
+
637
964
  # Handle PostgreSQL specific default format.
638
965
  def column_schema_normalize_default(default, type)
639
- if m = POSTGRES_DEFAULT_RE.match(default)
966
+ if m = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/.match(default)
640
967
  default = m[1] || m[2]
641
968
  end
642
969
  super(default, type)
@@ -658,14 +985,15 @@ module Sequel
658
985
  (super || op[:op] == :validate_constraint) && op[:op] != :rename_column
659
986
  end
660
987
 
661
- VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze
988
+ VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
662
989
  # The SQL queries to execute when starting a new connection.
663
- def connection_configuration_sqls
990
+ def connection_configuration_sqls(opts=@opts)
664
991
  sqls = []
665
992
 
666
- sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(@opts.fetch(:force_standard_strings, Postgres.force_standard_strings))
993
+ sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(opts.fetch(:force_standard_strings, true))
667
994
 
668
- if (cmm = @opts.fetch(:client_min_messages, Postgres.client_min_messages)) && !cmm.to_s.empty?
995
+ cmm = opts.fetch(:client_min_messages, :warning)
996
+ if cmm && !cmm.to_s.empty?
669
997
  cmm = cmm.to_s.upcase.strip
670
998
  unless VALID_CLIENT_MIN_MESSAGES.include?(cmm)
671
999
  raise Error, "Unsupported client_min_messages setting: #{cmm}"
@@ -673,7 +1001,7 @@ module Sequel
673
1001
  sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
674
1002
  end
675
1003
 
676
- if search_path = @opts[:search_path]
1004
+ if search_path = opts[:search_path]
677
1005
  case search_path
678
1006
  when String
679
1007
  search_path = search_path.split(",").map(&:strip)
@@ -693,7 +1021,8 @@ module Sequel
693
1021
  case constraint[:type]
694
1022
  when :exclude
695
1023
  elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
696
- sql = "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
1024
+ sql = String.new
1025
+ sql << "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
697
1026
  constraint_deferrable_sql_append(sql, constraint[:deferrable])
698
1027
  sql
699
1028
  when :foreign_key, :check
@@ -707,37 +1036,13 @@ module Sequel
707
1036
  end
708
1037
  end
709
1038
 
710
- # Callback used when conversion procs are updated.
711
- def conversion_procs_updated
712
- nil
713
- end
714
-
715
- # Convert the hash of named conversion procs into a hash a oid conversion procs.
716
- def convert_named_procs_to_procs(named_procs)
717
- h = {}
718
- from(:pg_type).where(:typtype=>['b', 'e'], :typname=>named_procs.keys.map(&:to_s)).select_map([:oid, :typname]).each do |oid, name|
719
- h[oid.to_i] = named_procs[name.untaint.to_sym]
720
- end
721
- h
722
- end
723
-
724
- # Copy the conversion procs related to the given oids from PG_TYPES into
725
- # the conversion procs for this instance.
726
- def copy_conversion_procs(oids)
727
- procs = conversion_procs
728
- oids.each do |oid|
729
- procs[oid] = PG_TYPES[oid]
730
- end
731
- conversion_procs_updated
732
- end
733
-
734
- EXCLUSION_CONSTRAINT_SQL_STATE = '23P01'.freeze
735
- DEADLOCK_SQL_STATE = '40P01'.freeze
736
1039
  def database_specific_error_class_from_sqlstate(sqlstate)
737
- if sqlstate == EXCLUSION_CONSTRAINT_SQL_STATE
1040
+ if sqlstate == '23P01'
738
1041
  ExclusionConstraintViolation
739
- elsif sqlstate == DEADLOCK_SQL_STATE
1042
+ elsif sqlstate == '40P01'
740
1043
  SerializationFailure
1044
+ elsif sqlstate == '55P03'
1045
+ DatabaseLockTimeout
741
1046
  else
742
1047
  super
743
1048
  end
@@ -753,6 +1058,7 @@ module Sequel
753
1058
  [/violates not-null constraint/, NotNullConstraintViolation],
754
1059
  [/conflicting key value violates exclusion constraint/, ExclusionConstraintViolation],
755
1060
  [/could not serialize access/, SerializationFailure],
1061
+ [/could not obtain lock on row in relation/, DatabaseLockTimeout],
756
1062
  ].freeze
757
1063
  def database_error_regexps
758
1064
  DATABASE_ERROR_REGEXPS
@@ -760,7 +1066,8 @@ module Sequel
760
1066
 
761
1067
  # SQL for doing fast table insert from stdin.
762
1068
  def copy_into_sql(table, opts)
763
- sql = "COPY #{literal(table)}"
1069
+ sql = String.new
1070
+ sql << "COPY #{literal(table)}"
764
1071
  if cols = opts[:columns]
765
1072
  sql << literal(Array(cols))
766
1073
  end
@@ -780,7 +1087,8 @@ module Sequel
780
1087
  table
781
1088
  else
782
1089
  if opts[:options] || opts[:format]
783
- options = " ("
1090
+ options = String.new
1091
+ options << " ("
784
1092
  options << "FORMAT #{opts[:format]}" if opts[:format]
785
1093
  options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
786
1094
  options << ')'
@@ -820,6 +1128,36 @@ module Sequel
820
1128
  "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]}"
821
1129
  end
822
1130
 
1131
+ # Create a partition of another table, used when the create_table with
1132
+ # the :partition_of option is given.
1133
+ def create_partition_of_table_from_generator(name, generator, options)
1134
+ execute_ddl(create_partition_of_table_sql(name, generator, options))
1135
+ end
1136
+
1137
+ # SQL for creating a partition of another table.
1138
+ def create_partition_of_table_sql(name, generator, options)
1139
+ sql = create_table_prefix_sql(name, options).dup
1140
+
1141
+ sql << " PARTITION OF #{quote_schema_table(options[:partition_of])}"
1142
+
1143
+ case generator.partition_type
1144
+ when :range
1145
+ from, to = generator.range
1146
+ sql << " FOR VALUES FROM #{literal(from)} TO #{literal(to)}"
1147
+ when :list
1148
+ sql << " FOR VALUES IN #{literal(generator.list)}"
1149
+ when :hash
1150
+ mod, remainder = generator.hash_values
1151
+ sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
1152
+ when :default
1153
+ sql << " DEFAULT"
1154
+ end
1155
+
1156
+ sql << create_table_suffix_sql(name, options)
1157
+
1158
+ sql
1159
+ end
1160
+
823
1161
  # SQL for creating a schema.
824
1162
  def create_schema_sql(name, opts=OPTS)
825
1163
  "CREATE SCHEMA #{'IF NOT EXISTS ' if opts[:if_not_exists]}#{quote_identifier(name)}#{" AUTHORIZATION #{literal(opts[:owner])}" if opts[:owner]}"
@@ -835,25 +1173,40 @@ module Sequel
835
1173
  raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
836
1174
  'FOREIGN '
837
1175
  elsif options[:unlogged]
838
- UNLOGGED
1176
+ 'UNLOGGED '
839
1177
  end
840
1178
 
841
1179
  "CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
842
1180
  end
843
1181
 
1182
+ # SQL for creating a table with PostgreSQL specific options
844
1183
  def create_table_sql(name, generator, options)
845
- sql = super
1184
+ "#{super}#{create_table_suffix_sql(name, options)}"
1185
+ end
1186
+
1187
+ # Handle various PostgreSQl specific table extensions such as inheritance,
1188
+ # partitioning, tablespaces, and foreign tables.
1189
+ def create_table_suffix_sql(name, options)
1190
+ sql = String.new
846
1191
 
847
1192
  if inherits = options[:inherits]
848
1193
  sql << " INHERITS (#{Array(inherits).map{|t| quote_schema_table(t)}.join(', ')})"
849
1194
  end
850
1195
 
1196
+ if partition_by = options[:partition_by]
1197
+ sql << " PARTITION BY #{options[:partition_type]||'RANGE'} #{literal(Array(partition_by))}"
1198
+ end
1199
+
851
1200
  if on_commit = options[:on_commit]
852
1201
  raise(Error, "can't provide :on_commit without :temp to create_table") unless options[:temp]
853
1202
  raise(Error, "unsupported on_commit option: #{on_commit.inspect}") unless ON_COMMIT.has_key?(on_commit)
854
1203
  sql << " ON COMMIT #{ON_COMMIT[on_commit]}"
855
1204
  end
856
1205
 
1206
+ if tablespace = options[:tablespace]
1207
+ sql << " TABLESPACE #{quote_identifier(tablespace)}"
1208
+ end
1209
+
857
1210
  if server = options[:foreign]
858
1211
  sql << " SERVER #{quote_identifier(server)}"
859
1212
  if foreign_opts = options[:options]
@@ -867,12 +1220,11 @@ module Sequel
867
1220
  def create_table_as_sql(name, sql, options)
868
1221
  result = create_table_prefix_sql name, options
869
1222
  if on_commit = options[:on_commit]
870
- result << " ON COMMIT #{ON_COMMIT[on_commit]}"
1223
+ result += " ON COMMIT #{ON_COMMIT[on_commit]}"
871
1224
  end
872
- result << " AS #{sql}"
1225
+ result += " AS #{sql}"
873
1226
  end
874
1227
 
875
- # Use a PostgreSQL-specific create table generator
876
1228
  def create_table_generator_class
877
1229
  Postgres::CreateTableGenerator
878
1230
  end
@@ -890,12 +1242,13 @@ module Sequel
890
1242
 
891
1243
  # DDL fragment for initial part of CREATE VIEW statement
892
1244
  def create_view_prefix_sql(name, options)
893
- 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])
894
- end
1245
+ 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])
1246
+
1247
+ if tablespace = options[:tablespace]
1248
+ sql += " TABLESPACE #{quote_identifier(tablespace)}"
1249
+ end
895
1250
 
896
- # The errors that the main adapters can raise, depends on the adapter being used
897
- def database_error_classes
898
- CONVERTED_EXCEPTIONS
1251
+ sql
899
1252
  end
900
1253
 
901
1254
  # SQL for dropping a function from the database.
@@ -934,36 +1287,17 @@ module Sequel
934
1287
  "DROP #{'MATERIALIZED ' if opts[:materialized]}VIEW#{' IF EXISTS' if opts[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if opts[:cascade]}"
935
1288
  end
936
1289
 
937
- # If opts includes a :schema option, or a default schema is used, restrict the dataset to
938
- # that schema. Otherwise, just exclude the default PostgreSQL schemas except for public.
1290
+ # If opts includes a :schema option, use it, otherwise restrict the filter to only the
1291
+ # currently visible schemas.
939
1292
  def filter_schema(ds, opts)
940
1293
  expr = if schema = opts[:schema]
941
1294
  schema.to_s
942
1295
  else
943
1296
  Sequel.function(:any, Sequel.function(:current_schemas, false))
944
1297
  end
945
- ds.where(:pg_namespace__nspname=>expr)
946
- end
947
-
948
- # Return a hash with oid keys and callable values, used for converting types.
949
- def get_conversion_procs
950
- procs = PG_TYPES.dup
951
- procs[1184] = procs[1114] = method(:to_application_timestamp)
952
- add_named_conversion_procs(procs, PG_NAMED_TYPES)
953
- procs
1298
+ ds.where{{pg_namespace[:nspname]=>expr}}
954
1299
  end
955
1300
 
956
- # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
957
- def identifier_input_method_default
958
- nil
959
- end
960
-
961
- # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
962
- def identifier_output_method_default
963
- nil
964
- end
965
-
966
- # PostgreSQL specific index SQL.
967
1301
  def index_definition_sql(table_name, index)
968
1302
  cols = index[:columns]
969
1303
  index_name = index[:name] || default_index_name(table_name, cols)
@@ -972,6 +1306,7 @@ module Sequel
972
1306
  else
973
1307
  literal(Array(cols))
974
1308
  end
1309
+ if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
975
1310
  unique = "UNIQUE " if index[:unique]
976
1311
  index_type = index[:type]
977
1312
  filter = index[:where] || index[:filter]
@@ -983,33 +1318,33 @@ module Sequel
983
1318
  when :spatial
984
1319
  index_type = :gist
985
1320
  end
986
- "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}"
1321
+ "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]}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
987
1322
  end
988
1323
 
989
1324
  # Setup datastructures shared by all postgres adapters.
990
1325
  def initialize_postgres_adapter
991
1326
  @primary_keys = {}
992
1327
  @primary_key_sequences = {}
993
- @conversion_procs = PG_TYPES.dup
994
- reset_conversion_procs
1328
+ @supported_types = {}
1329
+ procs = @conversion_procs = CONVERSION_PROCS.dup
1330
+ procs[1184] = procs[1114] = method(:to_application_timestamp)
995
1331
  end
996
1332
 
997
1333
  # Backbone of the tables and views support.
998
1334
  def pg_class_relname(type, opts)
999
- ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
1335
+ ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
1000
1336
  ds = filter_schema(ds, opts)
1001
1337
  m = output_identifier_meth
1002
1338
  if block_given?
1003
1339
  yield(ds)
1004
1340
  elsif opts[:qualify]
1005
- ds.select_append(:pg_namespace__nspname).map{|r| Sequel.qualify(m.call(r[:nspname]), m.call(r[:relname]))}
1341
+ ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
1006
1342
  else
1007
1343
  ds.map{|r| m.call(r[:relname])}
1008
1344
  end
1009
1345
  end
1010
1346
 
1011
- # Use a dollar sign instead of question mark for the argument
1012
- # placeholder.
1347
+ # Use a dollar sign instead of question mark for the argument placeholder.
1013
1348
  def prepared_arg_placeholder
1014
1349
  PREPARED_ARG_PLACEHOLDER
1015
1350
  end
@@ -1036,8 +1371,7 @@ module Sequel
1036
1371
  Sequel.cast(expr.to_s,:regclass).cast(:oid)
1037
1372
  end
1038
1373
 
1039
- # Remove the cached entries for primary keys and sequences when a table is
1040
- # changed.
1374
+ # Remove the cached entries for primary keys and sequences when a table is changed.
1041
1375
  def remove_cached_schema(table)
1042
1376
  tab = quote_schema_table(table)
1043
1377
  Sequel.synchronize do
@@ -1053,7 +1387,6 @@ module Sequel
1053
1387
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
1054
1388
  end
1055
1389
 
1056
- # Recognize PostgreSQL interval type.
1057
1390
  def schema_column_type(db_type)
1058
1391
  case db_type
1059
1392
  when /\Ainterval\z/io
@@ -1068,24 +1401,35 @@ module Sequel
1068
1401
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
1069
1402
  def schema_parse_table(table_name, opts)
1070
1403
  m = output_identifier_meth(opts[:dataset])
1071
- ds = metadata_dataset.select(:pg_attribute__attname___name,
1072
- SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
1073
- SQL::Cast.new(:basetype__oid, :integer).as(:base_oid),
1074
- SQL::Function.new(:format_type, :basetype__oid, :pg_type__typtypmod).as(:db_base_type),
1075
- SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
1076
- SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
1077
- SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
1078
- SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)).
1404
+ oid = regclass_oid(table_name, opts)
1405
+ ds = metadata_dataset.select{[
1406
+ pg_attribute[:attname].as(:name),
1407
+ SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1408
+ SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1409
+ SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1410
+ SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1411
+ SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
1412
+ SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
1413
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
1079
1414
  from(:pg_class).
1080
1415
  join(:pg_attribute, :attrelid=>:oid).
1081
1416
  join(:pg_type, :oid=>:atttypid).
1082
- left_outer_join(:pg_type___basetype, :oid=>:typbasetype).
1083
- left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
1084
- left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
1085
- filter(:pg_attribute__attisdropped=>false).
1086
- filter{|o| o.pg_attribute__attnum > 0}.
1087
- filter(:pg_class__oid=>regclass_oid(table_name, opts)).
1088
- order(:pg_attribute__attnum)
1417
+ left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
1418
+ left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
1419
+ left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
1420
+ where{{pg_attribute[:attisdropped]=>false}}.
1421
+ where{pg_attribute[:attnum] > 0}.
1422
+ where{{pg_class[:oid]=>oid}}.
1423
+ order{pg_attribute[:attnum]}
1424
+
1425
+ if server_version > 100000
1426
+ ds = ds.select_append{pg_attribute[:attidentity]}
1427
+
1428
+ if server_version > 120000
1429
+ ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
1430
+ end
1431
+ end
1432
+
1089
1433
  ds.map do |row|
1090
1434
  row[:default] = nil if blank_object?(row[:default])
1091
1435
  if row[:base_oid]
@@ -1098,8 +1442,9 @@ module Sequel
1098
1442
  row.delete(:db_base_type)
1099
1443
  end
1100
1444
  row[:type] = schema_column_type(row[:db_type])
1445
+ identity = row.delete(:attidentity)
1101
1446
  if row[:primary_key]
1102
- row[:auto_increment] = !!(row[:default] =~ /\Anextval/io)
1447
+ row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
1103
1448
  end
1104
1449
  [m.call(row.delete(:name)), row]
1105
1450
  end
@@ -1111,7 +1456,8 @@ module Sequel
1111
1456
  read_only = opts[:read_only]
1112
1457
  deferrable = opts[:deferrable]
1113
1458
  if level || !read_only.nil? || !deferrable.nil?
1114
- sql = "SET TRANSACTION"
1459
+ sql = String.new
1460
+ sql << "SET TRANSACTION"
1115
1461
  sql << " ISOLATION LEVEL #{Sequel::Database::TRANSACTION_ISOLATION_LEVELS[level]}" if level
1116
1462
  sql << " READ #{read_only ? 'ONLY' : 'WRITE'}" unless read_only.nil?
1117
1463
  sql << " #{'NOT ' unless deferrable}DEFERRABLE" unless deferrable.nil?
@@ -1135,7 +1481,7 @@ module Sequel
1135
1481
  end
1136
1482
 
1137
1483
  # Handle bigserial type if :serial option is present
1138
- def type_literal_generic_bignum(column)
1484
+ def type_literal_generic_bignum_symbol(column)
1139
1485
  column[:serial] ? :bigserial : super
1140
1486
  end
1141
1487
 
@@ -1152,7 +1498,7 @@ module Sequel
1152
1498
  # PostgreSQL prefers the text datatype. If a fixed size is requested,
1153
1499
  # the char type is used. If the text type is specifically
1154
1500
  # disallowed or there is a size specified, use the varchar type.
1155
- # Otherwise use the type type.
1501
+ # Otherwise use the text type.
1156
1502
  def type_literal_generic_string(column)
1157
1503
  if column[:fixed]
1158
1504
  "char(#{column[:size]||255})"
@@ -1169,72 +1515,17 @@ module Sequel
1169
1515
  end
1170
1516
  end
1171
1517
 
1172
- # Instance methods for datasets that connect to a PostgreSQL database.
1173
1518
  module DatasetMethods
1174
- ACCESS_SHARE = 'ACCESS SHARE'.freeze
1175
- ACCESS_EXCLUSIVE = 'ACCESS EXCLUSIVE'.freeze
1176
- BOOL_FALSE = 'false'.freeze
1177
- BOOL_TRUE = 'true'.freeze
1178
- COMMA_SEPARATOR = ', '.freeze
1179
- EXCLUSIVE = 'EXCLUSIVE'.freeze
1180
- EXPLAIN = 'EXPLAIN '.freeze
1181
- EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
1182
- FOR_SHARE = ' FOR SHARE'.freeze
1519
+ include UnmodifiedIdentifiers::DatasetMethods
1520
+
1183
1521
  NULL = LiteralString.new('NULL').freeze
1184
- PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
1185
- QUERY_PLAN = 'QUERY PLAN'.to_sym
1186
- ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
1187
- ROW_SHARE = 'ROW SHARE'.freeze
1188
- SHARE = 'SHARE'.freeze
1189
- SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
1190
- SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
1191
- SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
1192
- SPACE = Dataset::SPACE
1193
- FROM = Dataset::FROM
1194
- APOS = Dataset::APOS
1195
- APOS_RE = Dataset::APOS_RE
1196
- DOUBLE_APOS = Dataset::DOUBLE_APOS
1197
- PAREN_OPEN = Dataset::PAREN_OPEN
1198
- PAREN_CLOSE = Dataset::PAREN_CLOSE
1199
- COMMA = Dataset::COMMA
1200
- ESCAPE = Dataset::ESCAPE
1201
- BACKSLASH = Dataset::BACKSLASH
1202
- AS = Dataset::AS
1203
- XOR_OP = ' # '.freeze
1204
- CRLF = "\r\n".freeze
1205
- BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
1206
- WINDOW = " WINDOW ".freeze
1207
- SELECT_VALUES = "VALUES ".freeze
1208
- EMPTY_STRING = ''.freeze
1209
- LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze)
1522
+ LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
1210
1523
 
1211
1524
  Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
1212
1525
  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']])
1213
1526
  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']])
1214
1527
  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']])
1215
1528
 
1216
- # Shared methods for prepared statements when used with PostgreSQL databases.
1217
- module PreparedStatementMethods
1218
- # Override insert action to use RETURNING if the server supports it.
1219
- def run
1220
- if @prepared_type == :insert && (opts[:returning_pk] || !opts[:returning])
1221
- fetch_rows(prepared_sql){|r| return r.values.first}
1222
- else
1223
- super
1224
- end
1225
- end
1226
-
1227
- def prepared_sql
1228
- return @prepared_sql if @prepared_sql
1229
- if @prepared_type == :insert && !opts[:returning]
1230
- @opts[:returning] = insert_pk
1231
- @opts[:returning_pk] = true
1232
- end
1233
- super
1234
- @prepared_sql
1235
- end
1236
- end
1237
-
1238
1529
  # Return the results of an EXPLAIN ANALYZE query as a string
1239
1530
  def analyze
1240
1531
  explain(:analyze=>true)
@@ -1246,7 +1537,7 @@ module Sequel
1246
1537
  def complex_expression_sql_append(sql, op, args)
1247
1538
  case op
1248
1539
  when :^
1249
- j = XOR_OP
1540
+ j = ' # '
1250
1541
  c = false
1251
1542
  args.each do |a|
1252
1543
  sql << j if c
@@ -1254,13 +1545,13 @@ module Sequel
1254
1545
  c ||= true
1255
1546
  end
1256
1547
  when :ILIKE, :'NOT ILIKE'
1257
- sql << PAREN_OPEN
1258
- literal_append(sql, args.at(0))
1259
- sql << SPACE << op.to_s << SPACE
1260
- literal_append(sql, args.at(1))
1261
- sql << ESCAPE
1262
- literal_append(sql, BACKSLASH)
1263
- sql << PAREN_CLOSE
1548
+ sql << '('
1549
+ literal_append(sql, args[0])
1550
+ sql << ' ' << op.to_s << ' '
1551
+ literal_append(sql, args[1])
1552
+ sql << " ESCAPE "
1553
+ literal_append(sql, "\\")
1554
+ sql << ')'
1264
1555
  else
1265
1556
  super
1266
1557
  end
@@ -1286,7 +1577,7 @@ module Sequel
1286
1577
 
1287
1578
  # Return the results of an EXPLAIN query as a string
1288
1579
  def explain(opts=OPTS)
1289
- with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
1580
+ with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
1290
1581
  end
1291
1582
 
1292
1583
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
@@ -1298,6 +1589,8 @@ module Sequel
1298
1589
  # of any of the terms in any of the cols.
1299
1590
  #
1300
1591
  # Options:
1592
+ # :headline :: Append a expression to the selected columns aliased to headline that
1593
+ # contains an extract of the matched text.
1301
1594
  # :language :: The language to use for the search (default: 'simple')
1302
1595
  # :plain :: Whether a plain search should be used (default: false). In this case,
1303
1596
  # terms should be a single string, and it will do a search where cols
@@ -1305,6 +1598,8 @@ module Sequel
1305
1598
  # :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
1306
1599
  # returned rows also include the exact phrase used.
1307
1600
  # :rank :: Set to true to order by the rank, so that closer matches are returned first.
1601
+ # :to_tsquery :: Can be set to :plain or :phrase to specify the function to use to
1602
+ # convert the terms to a ts_query.
1308
1603
  # :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
1309
1604
  # tsquery, and can be used directly in the query.
1310
1605
  # :tsvector :: Specifies the cols argument is already a valid SQL expression returning a
@@ -1319,11 +1614,18 @@ module Sequel
1319
1614
 
1320
1615
  unless opts[:tsquery]
1321
1616
  phrase_terms = terms.is_a?(Array) ? terms.join(' | ') : terms
1322
- query_func = (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1617
+
1618
+ query_func = case to_tsquery = opts[:to_tsquery]
1619
+ when :phrase, :plain
1620
+ :"#{to_tsquery}to_tsquery"
1621
+ else
1622
+ (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1623
+ end
1624
+
1323
1625
  terms = Sequel.function(query_func, lang, phrase_terms)
1324
1626
  end
1325
1627
 
1326
- ds = where(Sequel.lit(["(", " @@ ", ")"], cols, terms))
1628
+ ds = where(Sequel.lit(["", " @@ ", ""], cols, terms))
1327
1629
 
1328
1630
  if opts[:phrase]
1329
1631
  raise Error, "can't use :phrase with either :tsvector or :tsquery arguments to full_text_search together" if opts[:tsvector] || opts[:tsquery]
@@ -1331,7 +1633,11 @@ module Sequel
1331
1633
  end
1332
1634
 
1333
1635
  if opts[:rank]
1334
- ds = ds.order{ts_rank_cd(cols, terms)}
1636
+ ds = ds.reverse{ts_rank_cd(cols, terms)}
1637
+ end
1638
+
1639
+ if opts[:headline]
1640
+ ds = ds.select_append{ts_headline(lang, phrase_cols, terms).as(:headline)}
1335
1641
  end
1336
1642
 
1337
1643
  ds
@@ -1356,6 +1662,7 @@ module Sequel
1356
1662
 
1357
1663
  # Handle uniqueness violations when inserting, by updating the conflicting row, using
1358
1664
  # ON CONFLICT. With no options, uses ON CONFLICT DO NOTHING. Options:
1665
+ # :conflict_where :: The index filter, when using a partial index to determine uniqueness.
1359
1666
  # :constraint :: An explicit constraint name, has precendence over :target.
1360
1667
  # :target :: The column name or expression to handle uniqueness violations on.
1361
1668
  # :update :: A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
@@ -1363,24 +1670,28 @@ module Sequel
1363
1670
  #
1364
1671
  # Examples:
1365
1672
  #
1366
- # DB[:table].insert_conflict.insert(:a=>1, :b=>2)
1673
+ # DB[:table].insert_conflict.insert(a: 1, b: 2)
1367
1674
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1368
1675
  # # ON CONFLICT DO NOTHING
1369
1676
  #
1370
- # DB[:table].insert_conflict(:constraint=>:table_a_uidx).insert(:a=>1, :b=>2)
1677
+ # DB[:table].insert_conflict(constraint: :table_a_uidx).insert(a: 1, b: 2)
1371
1678
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1372
1679
  # # ON CONFLICT ON CONSTRAINT table_a_uidx DO NOTHING
1373
1680
  #
1374
- # DB[:table].insert_conflict(:target=>:a).insert(:a=>1, :b=>2)
1681
+ # DB[:table].insert_conflict(target: :a).insert(a: 1, b: 2)
1375
1682
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1376
1683
  # # ON CONFLICT (a) DO NOTHING
1684
+ #
1685
+ # DB[:table].insert_conflict(target: :a, conflict_where: {c: true}).insert(a: 1, b: 2)
1686
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1687
+ # # ON CONFLICT (a) WHERE (c IS TRUE) DO NOTHING
1377
1688
  #
1378
- # DB[:table].insert_conflict(:target=>:a, :update=>{:b=>:excluded__b}).insert(:a=>1, :b=>2)
1689
+ # DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
1379
1690
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1380
1691
  # # ON CONFLICT (a) DO UPDATE SET b = excluded.b
1381
1692
  #
1382
- # DB[:table].insert_conflict(:constraint=>:table_a_uidx,
1383
- # :update=>{:b=>:excluded__b}, :update_where=>{:table__status_id=>1}).insert(:a=>1, :b=>2)
1693
+ # DB[:table].insert_conflict(constraint: :table_a_uidx,
1694
+ # update: {b: Sequel[:excluded][:b]}, update_where: {Sequel[:table][:status_id] => 1}).insert(a: 1, b: 2)
1384
1695
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1385
1696
  # # ON CONFLICT ON CONSTRAINT table_a_uidx
1386
1697
  # # DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
@@ -1391,18 +1702,20 @@ module Sequel
1391
1702
  # Ignore uniqueness/exclusion violations when inserting, using ON CONFLICT DO NOTHING.
1392
1703
  # Exists mostly for compatibility to MySQL's insert_ignore. Example:
1393
1704
  #
1394
- # DB[:table].insert_ignore.insert(:a=>1, :b=>2)
1705
+ # DB[:table].insert_ignore.insert(a: 1, b: 2)
1395
1706
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1396
1707
  # # ON CONFLICT DO NOTHING
1397
1708
  def insert_ignore
1398
1709
  insert_conflict
1399
1710
  end
1400
1711
 
1401
- # Insert a record returning the record inserted. Always returns nil without
1402
- # inserting a query if disable_insert_returning is used.
1712
+ # Insert a record, returning the record inserted, using RETURNING. Always returns nil without
1713
+ # running an INSERT statement if disable_insert_returning is used. If the query runs
1714
+ # but returns no values, returns false.
1403
1715
  def insert_select(*values)
1404
1716
  return unless supports_insert_select?
1405
- server?(:default).with_sql_first(insert_select_sql(*values))
1717
+ # Handle case where query does not return a row
1718
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
1406
1719
  end
1407
1720
 
1408
1721
  # The SQL to use for an insert_select, adds a RETURNING clause to the insert
@@ -1414,14 +1727,14 @@ module Sequel
1414
1727
 
1415
1728
  # Locks all tables in the dataset's FROM clause (but not in JOINs) with
1416
1729
  # the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
1417
- # a new transaction, locks the table, and yields. If a block is not given
1730
+ # a new transaction, locks the table, and yields. If a block is not given,
1418
1731
  # just locks the tables. Note that PostgreSQL will probably raise an error
1419
1732
  # if you lock the table outside of an existing transaction. Returns nil.
1420
1733
  def lock(mode, opts=OPTS)
1421
1734
  if block_given? # perform locking inside a transaction and yield to block
1422
1735
  @db.transaction(opts){lock(mode, opts); yield}
1423
1736
  else
1424
- sql = 'LOCK TABLE '
1737
+ sql = 'LOCK TABLE '.dup
1425
1738
  source_list_append(sql, @opts[:from])
1426
1739
  mode = mode.to_s.upcase.strip
1427
1740
  unless LOCK_MODES.include?(mode)
@@ -1433,6 +1746,19 @@ module Sequel
1433
1746
  nil
1434
1747
  end
1435
1748
 
1749
+ # Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
1750
+ # always use the user supplied value, and an error is not raised for identity
1751
+ # columns that are GENERATED ALWAYS.
1752
+ def overriding_system_value
1753
+ clone(:override=>:system)
1754
+ end
1755
+
1756
+ # Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
1757
+ # always use the sequence value instead of the user supplied value.
1758
+ def overriding_user_value
1759
+ clone(:override=>:user)
1760
+ end
1761
+
1436
1762
  def supports_cte?(type=:select)
1437
1763
  if type == :select
1438
1764
  server_version >= 80400
@@ -1477,7 +1803,7 @@ module Sequel
1477
1803
  server_version >= 90500
1478
1804
  end
1479
1805
 
1480
- # PostgreSQL 9.3rc1+ supports lateral subqueries
1806
+ # PostgreSQL 9.3+ supports lateral subqueries
1481
1807
  def supports_lateral_subqueries?
1482
1808
  server_version >= 90300
1483
1809
  end
@@ -1487,6 +1813,11 @@ module Sequel
1487
1813
  true
1488
1814
  end
1489
1815
 
1816
+ # PostgreSQL supports NOWAIT.
1817
+ def supports_nowait?
1818
+ true
1819
+ end
1820
+
1490
1821
  # Returning is always supported.
1491
1822
  def supports_returning?(type)
1492
1823
  true
@@ -1497,16 +1828,39 @@ module Sequel
1497
1828
  true
1498
1829
  end
1499
1830
 
1831
+ # PostgreSQL 9.5+ supports SKIP LOCKED.
1832
+ def supports_skip_locked?
1833
+ server_version >= 90500
1834
+ end
1835
+
1500
1836
  # PostgreSQL supports timezones in literal timestamps
1501
1837
  def supports_timestamp_timezones?
1502
1838
  true
1503
1839
  end
1504
1840
 
1841
+ # PostgreSQL 8.4+ supports WINDOW clause.
1842
+ def supports_window_clause?
1843
+ server_version >= 80400
1844
+ end
1845
+
1505
1846
  # PostgreSQL 8.4+ supports window functions
1506
1847
  def supports_window_functions?
1507
1848
  server_version >= 80400
1508
1849
  end
1509
1850
 
1851
+ # Base support added in 8.4, offset supported added in 9.0,
1852
+ # GROUPS and EXCLUDE support added in 11.0.
1853
+ def supports_window_function_frame_option?(option)
1854
+ case option
1855
+ when :rows, :range
1856
+ true
1857
+ when :offset
1858
+ server_version >= 90000
1859
+ when :groups, :exclude
1860
+ server_version >= 110000
1861
+ end
1862
+ end
1863
+
1510
1864
  # Truncates the dataset. Returns nil.
1511
1865
  #
1512
1866
  # Options:
@@ -1518,10 +1872,11 @@ module Sequel
1518
1872
  # :only and :restart only work correctly on PostgreSQL 8.4+.
1519
1873
  #
1520
1874
  # Usage:
1521
- # DB[:table].truncate # TRUNCATE TABLE "table"
1522
- # # => nil
1523
- # DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
1524
- # # => nil
1875
+ # DB[:table].truncate
1876
+ # # TRUNCATE TABLE "table"
1877
+ #
1878
+ # DB[:table].truncate(cascade: true, only: true, restart: true)
1879
+ # # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
1525
1880
  def truncate(opts = OPTS)
1526
1881
  if opts.empty?
1527
1882
  super()
@@ -1530,9 +1885,11 @@ module Sequel
1530
1885
  end
1531
1886
  end
1532
1887
 
1533
- # Return a clone of the dataset with an addition named window that can be referenced in window functions.
1534
- def window(name, opts)
1535
- clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
1888
+ # Use WITH TIES when limiting the result set to also include additional
1889
+ # rules that have the same results for the order column as the final row.
1890
+ # Requires PostgreSQL 13.
1891
+ def with_ties
1892
+ clone(:limit_with_ties=>true)
1536
1893
  end
1537
1894
 
1538
1895
  protected
@@ -1544,7 +1901,9 @@ module Sequel
1544
1901
  def _import(columns, values, opts=OPTS)
1545
1902
  if @opts[:returning]
1546
1903
  statements = multi_insert_sql(columns, values)
1547
- @db.transaction(Hash[opts].merge!(:server=>@opts[:server])) do
1904
+ trans_opts = Hash[opts]
1905
+ trans_opts[:server] = @opts[:server]
1906
+ @db.transaction(trans_opts) do
1548
1907
  statements.map{|st| returning_fetch_rows(st)}
1549
1908
  end.first.map{|v| v.length == 1 ? v.values.first : v}
1550
1909
  elsif opts[:return] == :primary_key
@@ -1554,11 +1913,19 @@ module Sequel
1554
1913
  end
1555
1914
  end
1556
1915
 
1916
+ def to_prepared_statement(type, *a)
1917
+ if type == :insert && !@opts.has_key?(:returning)
1918
+ returning(insert_pk).send(:to_prepared_statement, :insert_pk, *a)
1919
+ else
1920
+ super
1921
+ end
1922
+ end
1923
+
1557
1924
  private
1558
1925
 
1559
1926
  # Format TRUNCATE statement with PostgreSQL specific options.
1560
1927
  def _truncate_sql(table)
1561
- to = @opts[:truncate_opts] || {}
1928
+ to = @opts[:truncate_opts] || OPTS
1562
1929
  "TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
1563
1930
  end
1564
1931
 
@@ -1570,7 +1937,7 @@ module Sequel
1570
1937
 
1571
1938
  # Only include the primary table in the main delete clause
1572
1939
  def delete_from_sql(sql)
1573
- sql << FROM
1940
+ sql << ' FROM '
1574
1941
  source_list_append(sql, @opts[:from][0..0])
1575
1942
  end
1576
1943
 
@@ -1588,22 +1955,34 @@ module Sequel
1588
1955
  sql << " ON CONSTRAINT "
1589
1956
  identifier_append(sql, target)
1590
1957
  elsif target = opts[:target]
1591
- sql << ' ('
1592
- identifier_append(sql, target)
1593
- sql << ')'
1958
+ sql << ' '
1959
+ identifier_append(sql, Array(target))
1960
+ if conflict_where = opts[:conflict_where]
1961
+ sql << " WHERE "
1962
+ literal_append(sql, conflict_where)
1963
+ end
1594
1964
  end
1595
1965
 
1596
1966
  if values = opts[:update]
1597
1967
  sql << " DO UPDATE SET "
1598
1968
  update_sql_values_hash(sql, values)
1599
- if where = opts[:update_where]
1969
+ if update_where = opts[:update_where]
1600
1970
  sql << " WHERE "
1601
- literal_append(sql, where)
1971
+ literal_append(sql, update_where)
1602
1972
  end
1603
1973
  else
1604
1974
  sql << " DO NOTHING"
1605
1975
  end
1976
+ end
1977
+ end
1606
1978
 
1979
+ # Include aliases when inserting into a single table on PostgreSQL 9.5+.
1980
+ def insert_into_sql(sql)
1981
+ sql << " INTO "
1982
+ if (f = @opts[:from]) && f.length == 1
1983
+ identifier_append(sql, server_version >= 90500 ? f.first : unaliased_identifier(f.first))
1984
+ else
1985
+ source_list_append(sql, f)
1607
1986
  end
1608
1987
  end
1609
1988
 
@@ -1619,13 +1998,24 @@ module Sequel
1619
1998
  end
1620
1999
  end
1621
2000
 
2001
+ # Support OVERRIDING SYSTEM|USER VALUE in insert statements
2002
+ def insert_values_sql(sql)
2003
+ case opts[:override]
2004
+ when :system
2005
+ sql << " OVERRIDING SYSTEM VALUE"
2006
+ when :user
2007
+ sql << " OVERRIDING USER VALUE"
2008
+ end
2009
+ super
2010
+ end
2011
+
1622
2012
  # For multiple table support, PostgreSQL requires at least
1623
2013
  # two from tables, with joins allowed.
1624
2014
  def join_from_sql(type, sql)
1625
2015
  if(from = @opts[:from][1..-1]).empty?
1626
2016
  raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
1627
2017
  else
1628
- sql << SPACE << type.to_s << SPACE
2018
+ sql << ' ' << type.to_s << ' '
1629
2019
  source_list_append(sql, from)
1630
2020
  select_join_sql(sql)
1631
2021
  end
@@ -1633,12 +2023,12 @@ module Sequel
1633
2023
 
1634
2024
  # Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
1635
2025
  def literal_blob_append(sql, v)
1636
- sql << APOS << v.gsub(BLOB_RE){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << APOS
2026
+ sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
1637
2027
  end
1638
2028
 
1639
2029
  # PostgreSQL uses FALSE for false values
1640
2030
  def literal_false
1641
- BOOL_FALSE
2031
+ 'false'
1642
2032
  end
1643
2033
 
1644
2034
  # PostgreSQL quotes NaN and Infinity.
@@ -1656,12 +2046,12 @@ module Sequel
1656
2046
 
1657
2047
  # Assume that SQL standard quoting is on, per Sequel's defaults
1658
2048
  def literal_string_append(sql, v)
1659
- sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
2049
+ sql << "'" << v.gsub("'", "''") << "'"
1660
2050
  end
1661
2051
 
1662
- # PostgreSQL uses FALSE for false values
2052
+ # PostgreSQL uses true for true values
1663
2053
  def literal_true
1664
- BOOL_TRUE
2054
+ 'true'
1665
2055
  end
1666
2056
 
1667
2057
  # PostgreSQL supports multiple rows in INSERT.
@@ -1669,45 +2059,96 @@ module Sequel
1669
2059
  :values
1670
2060
  end
1671
2061
 
2062
+ # Dataset options that do not affect the generated SQL.
2063
+ def non_sql_option?(key)
2064
+ super || key == :cursor || key == :insert_conflict
2065
+ end
2066
+
1672
2067
  # PostgreSQL requires parentheses around compound datasets if they use
1673
2068
  # CTEs, and using them in other places doesn't hurt.
1674
2069
  def compound_dataset_sql_append(sql, ds)
1675
- sql << PAREN_OPEN
2070
+ sql << '('
1676
2071
  super
1677
- sql << PAREN_CLOSE
2072
+ sql << ')'
2073
+ end
2074
+
2075
+ # Backslash is supported by default as the escape character on PostgreSQL,
2076
+ # and using ESCAPE can break LIKE ANY() usage.
2077
+ def requires_like_escape?
2078
+ false
2079
+ end
2080
+
2081
+ # Support FETCH FIRST WITH TIES on PostgreSQL 13+.
2082
+ def select_limit_sql(sql)
2083
+ l = @opts[:limit]
2084
+ o = @opts[:offset]
2085
+
2086
+ return unless l || o
2087
+
2088
+ if @opts[:limit_with_ties]
2089
+ if o
2090
+ sql << " OFFSET "
2091
+ literal_append(sql, o)
2092
+ end
2093
+
2094
+ if l
2095
+ sql << " FETCH FIRST "
2096
+ literal_append(sql, l)
2097
+ sql << " ROWS WITH TIES"
2098
+ end
2099
+ else
2100
+ if l
2101
+ sql << " LIMIT "
2102
+ literal_append(sql, l)
2103
+ end
2104
+
2105
+ if o
2106
+ sql << " OFFSET "
2107
+ literal_append(sql, o)
2108
+ end
2109
+ end
1678
2110
  end
1679
2111
 
1680
2112
  # Support FOR SHARE locking when using the :share lock style.
2113
+ # Use SKIP LOCKED if skipping locked rows.
1681
2114
  def select_lock_sql(sql)
1682
- @opts[:lock] == :share ? (sql << FOR_SHARE) : super
2115
+ lock = @opts[:lock]
2116
+ if lock == :share
2117
+ sql << ' FOR SHARE'
2118
+ else
2119
+ super
2120
+ end
2121
+
2122
+ if lock
2123
+ if @opts[:skip_locked]
2124
+ sql << " SKIP LOCKED"
2125
+ elsif @opts[:nowait]
2126
+ sql << " NOWAIT"
2127
+ end
2128
+ end
1683
2129
  end
1684
2130
 
1685
2131
  # Support VALUES clause instead of the SELECT clause to return rows.
1686
2132
  def select_values_sql(sql)
1687
- sql << SELECT_VALUES
2133
+ sql << "VALUES "
1688
2134
  expression_list_append(sql, opts[:values])
1689
2135
  end
1690
2136
 
1691
- # SQL fragment for named window specifications
1692
- def select_window_sql(sql)
1693
- if ws = @opts[:window]
1694
- sql << WINDOW
1695
- c = false
1696
- co = COMMA
1697
- as = AS
1698
- ws.map do |name, window|
1699
- sql << co if c
1700
- literal_append(sql, name)
1701
- sql << as
1702
- literal_append(sql, window)
1703
- c ||= true
1704
- end
1705
- end
1706
- end
1707
-
1708
2137
  # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
1709
2138
  def select_with_sql_base
1710
- opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
2139
+ opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
2140
+ end
2141
+
2142
+ # Support WITH AS [NOT] MATERIALIZED if :materialized option is used.
2143
+ def select_with_sql_prefix(sql, w)
2144
+ super
2145
+
2146
+ case w[:materialized]
2147
+ when true
2148
+ sql << "MATERIALIZED "
2149
+ when false
2150
+ sql << "NOT MATERIALIZED "
2151
+ end
1711
2152
  end
1712
2153
 
1713
2154
  # The version of the database server
@@ -1715,6 +2156,11 @@ module Sequel
1715
2156
  db.server_version(@opts[:server])
1716
2157
  end
1717
2158
 
2159
+ # PostgreSQL 9.4+ supports the FILTER clause for aggregate functions.
2160
+ def supports_filtered_aggregates?
2161
+ server_version >= 90400
2162
+ end
2163
+
1718
2164
  # PostgreSQL supports quoted function names.
1719
2165
  def supports_quoted_function_names?
1720
2166
  true
@@ -1722,8 +2168,8 @@ module Sequel
1722
2168
 
1723
2169
  # Concatenate the expressions with a space in between
1724
2170
  def full_text_string_join(cols)
1725
- cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, EMPTY_STRING)}
1726
- cols = cols.zip([SPACE] * cols.length).flatten
2171
+ cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
2172
+ cols = cols.zip([' '] * cols.length).flatten
1727
2173
  cols.pop
1728
2174
  SQL::StringExpression.new(:'||', *cols)
1729
2175
  end
@@ -1735,7 +2181,7 @@ module Sequel
1735
2181
 
1736
2182
  # Only include the primary table in the main update clause
1737
2183
  def update_table_sql(sql)
1738
- sql << SPACE
2184
+ sql << ' '
1739
2185
  source_list_append(sql, @opts[:from][0..0])
1740
2186
  end
1741
2187
  end