sequel 5.83.0 → 5.106.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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/lib/sequel/adapters/ado/access.rb +2 -2
  4. data/lib/sequel/adapters/ado.rb +2 -2
  5. data/lib/sequel/adapters/amalgalite.rb +16 -1
  6. data/lib/sequel/adapters/ibmdb.rb +2 -1
  7. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/derby.rb +4 -2
  9. data/lib/sequel/adapters/jdbc/h2.rb +4 -4
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  11. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
  13. data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  15. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
  18. data/lib/sequel/adapters/jdbc.rb +29 -15
  19. data/lib/sequel/adapters/mysql.rb +1 -1
  20. data/lib/sequel/adapters/mysql2.rb +10 -3
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +16 -0
  23. data/lib/sequel/adapters/postgres.rb +2 -1
  24. data/lib/sequel/adapters/shared/access.rb +1 -0
  25. data/lib/sequel/adapters/shared/db2.rb +8 -3
  26. data/lib/sequel/adapters/shared/mssql.rb +9 -8
  27. data/lib/sequel/adapters/shared/mysql.rb +16 -11
  28. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  29. data/lib/sequel/adapters/shared/postgres.rb +438 -47
  30. data/lib/sequel/adapters/shared/sqlite.rb +9 -7
  31. data/lib/sequel/adapters/sqlite.rb +20 -2
  32. data/lib/sequel/adapters/tinytds.rb +2 -2
  33. data/lib/sequel/adapters/trilogy.rb +2 -3
  34. data/lib/sequel/ast_transformer.rb +2 -0
  35. data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
  36. data/lib/sequel/connection_pool/timed_queue.rb +33 -10
  37. data/lib/sequel/connection_pool.rb +8 -2
  38. data/lib/sequel/core.rb +31 -2
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/dataset_defaults.rb +3 -3
  41. data/lib/sequel/database/misc.rb +29 -11
  42. data/lib/sequel/database/query.rb +12 -12
  43. data/lib/sequel/database/schema_generator.rb +117 -16
  44. data/lib/sequel/database/schema_methods.rb +85 -30
  45. data/lib/sequel/dataset/actions.rb +83 -11
  46. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  47. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
  48. data/lib/sequel/dataset/prepared_statements.rb +76 -28
  49. data/lib/sequel/dataset/query.rb +26 -11
  50. data/lib/sequel/dataset/sql.rb +40 -17
  51. data/lib/sequel/extensions/async_thread_pool.rb +1 -1
  52. data/lib/sequel/extensions/caller_logging.rb +1 -3
  53. data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
  54. data/lib/sequel/extensions/connection_expiration.rb +44 -5
  55. data/lib/sequel/extensions/connection_validator.rb +15 -10
  56. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  57. data/lib/sequel/extensions/dataset_run.rb +41 -0
  58. data/lib/sequel/extensions/date_arithmetic.rb +6 -6
  59. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  60. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  61. data/lib/sequel/extensions/inflector.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
  63. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  64. data/lib/sequel/extensions/migration.rb +38 -21
  65. data/lib/sequel/extensions/null_dataset.rb +2 -2
  66. data/lib/sequel/extensions/pg_array_ops.rb +41 -0
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
  68. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  69. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
  70. data/lib/sequel/extensions/pg_enum.rb +4 -4
  71. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  73. data/lib/sequel/extensions/pg_json_ops.rb +674 -18
  74. data/lib/sequel/extensions/pg_multirange.rb +1 -1
  75. data/lib/sequel/extensions/pg_range.rb +14 -2
  76. data/lib/sequel/extensions/pg_row.rb +5 -3
  77. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  78. data/lib/sequel/extensions/provenance.rb +1 -3
  79. data/lib/sequel/extensions/query_blocker.rb +172 -0
  80. data/lib/sequel/extensions/schema_caching.rb +24 -9
  81. data/lib/sequel/extensions/schema_dumper.rb +17 -5
  82. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  83. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  84. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  85. data/lib/sequel/extensions/string_agg.rb +2 -2
  86. data/lib/sequel/extensions/to_dot.rb +5 -0
  87. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  88. data/lib/sequel/model/associations.rb +215 -15
  89. data/lib/sequel/model/base.rb +198 -35
  90. data/lib/sequel/model/plugins.rb +12 -1
  91. data/lib/sequel/model.rb +1 -0
  92. data/lib/sequel/plugins/accessed_columns.rb +4 -0
  93. data/lib/sequel/plugins/active_model.rb +5 -6
  94. data/lib/sequel/plugins/association_pks.rb +2 -0
  95. data/lib/sequel/plugins/auto_validations.rb +2 -0
  96. data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
  97. data/lib/sequel/plugins/columns_updated.rb +4 -0
  98. data/lib/sequel/plugins/composition.rb +3 -1
  99. data/lib/sequel/plugins/constraint_validations.rb +15 -10
  100. data/lib/sequel/plugins/dataset_associations.rb +20 -1
  101. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  102. data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
  103. data/lib/sequel/plugins/dirty.rb +9 -2
  104. data/lib/sequel/plugins/enum.rb +1 -1
  105. data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
  106. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  107. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  108. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  109. data/lib/sequel/plugins/instance_filters.rb +8 -1
  110. data/lib/sequel/plugins/instance_hooks.rb +4 -0
  111. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  112. data/lib/sequel/plugins/json_serializer.rb +6 -10
  113. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  114. data/lib/sequel/plugins/many_through_many.rb +21 -0
  115. data/lib/sequel/plugins/modification_detection.rb +2 -0
  116. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  117. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  118. data/lib/sequel/plugins/paged_operations.rb +5 -2
  119. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  120. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  121. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  122. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  123. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
  124. data/lib/sequel/plugins/rcte_tree.rb +6 -6
  125. data/lib/sequel/plugins/serialization.rb +23 -6
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
  127. data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
  128. data/lib/sequel/plugins/split_values.rb +12 -0
  129. data/lib/sequel/plugins/sql_comments.rb +7 -2
  130. data/lib/sequel/plugins/static_cache.rb +13 -0
  131. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  132. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  133. data/lib/sequel/plugins/subset_static_cache.rb +278 -0
  134. data/lib/sequel/plugins/table_select.rb +7 -0
  135. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
  136. data/lib/sequel/plugins/unused_associations.rb +11 -3
  137. data/lib/sequel/plugins/update_primary_key.rb +4 -0
  138. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  139. data/lib/sequel/plugins/validation_contexts.rb +4 -0
  140. data/lib/sequel/sql.rb +27 -12
  141. data/lib/sequel/version.rb +1 -1
  142. metadata +17 -240
  143. data/CHANGELOG +0 -1393
  144. data/README.rdoc +0 -936
  145. data/doc/advanced_associations.rdoc +0 -884
  146. data/doc/association_basics.rdoc +0 -1859
  147. data/doc/bin_sequel.rdoc +0 -146
  148. data/doc/cheat_sheet.rdoc +0 -255
  149. data/doc/code_order.rdoc +0 -104
  150. data/doc/core_extensions.rdoc +0 -405
  151. data/doc/dataset_basics.rdoc +0 -96
  152. data/doc/dataset_filtering.rdoc +0 -222
  153. data/doc/extensions.rdoc +0 -77
  154. data/doc/fork_safety.rdoc +0 -84
  155. data/doc/mass_assignment.rdoc +0 -98
  156. data/doc/migration.rdoc +0 -660
  157. data/doc/model_dataset_method_design.rdoc +0 -129
  158. data/doc/model_hooks.rdoc +0 -254
  159. data/doc/model_plugins.rdoc +0 -270
  160. data/doc/mssql_stored_procedures.rdoc +0 -43
  161. data/doc/object_model.rdoc +0 -563
  162. data/doc/opening_databases.rdoc +0 -439
  163. data/doc/postgresql.rdoc +0 -611
  164. data/doc/prepared_statements.rdoc +0 -144
  165. data/doc/querying.rdoc +0 -1070
  166. data/doc/reflection.rdoc +0 -120
  167. data/doc/release_notes/5.0.0.txt +0 -159
  168. data/doc/release_notes/5.1.0.txt +0 -31
  169. data/doc/release_notes/5.10.0.txt +0 -84
  170. data/doc/release_notes/5.11.0.txt +0 -83
  171. data/doc/release_notes/5.12.0.txt +0 -141
  172. data/doc/release_notes/5.13.0.txt +0 -27
  173. data/doc/release_notes/5.14.0.txt +0 -63
  174. data/doc/release_notes/5.15.0.txt +0 -39
  175. data/doc/release_notes/5.16.0.txt +0 -110
  176. data/doc/release_notes/5.17.0.txt +0 -31
  177. data/doc/release_notes/5.18.0.txt +0 -69
  178. data/doc/release_notes/5.19.0.txt +0 -28
  179. data/doc/release_notes/5.2.0.txt +0 -33
  180. data/doc/release_notes/5.20.0.txt +0 -89
  181. data/doc/release_notes/5.21.0.txt +0 -87
  182. data/doc/release_notes/5.22.0.txt +0 -48
  183. data/doc/release_notes/5.23.0.txt +0 -56
  184. data/doc/release_notes/5.24.0.txt +0 -56
  185. data/doc/release_notes/5.25.0.txt +0 -32
  186. data/doc/release_notes/5.26.0.txt +0 -35
  187. data/doc/release_notes/5.27.0.txt +0 -21
  188. data/doc/release_notes/5.28.0.txt +0 -16
  189. data/doc/release_notes/5.29.0.txt +0 -22
  190. data/doc/release_notes/5.3.0.txt +0 -121
  191. data/doc/release_notes/5.30.0.txt +0 -20
  192. data/doc/release_notes/5.31.0.txt +0 -148
  193. data/doc/release_notes/5.32.0.txt +0 -46
  194. data/doc/release_notes/5.33.0.txt +0 -24
  195. data/doc/release_notes/5.34.0.txt +0 -40
  196. data/doc/release_notes/5.35.0.txt +0 -56
  197. data/doc/release_notes/5.36.0.txt +0 -60
  198. data/doc/release_notes/5.37.0.txt +0 -30
  199. data/doc/release_notes/5.38.0.txt +0 -28
  200. data/doc/release_notes/5.39.0.txt +0 -19
  201. data/doc/release_notes/5.4.0.txt +0 -80
  202. data/doc/release_notes/5.40.0.txt +0 -40
  203. data/doc/release_notes/5.41.0.txt +0 -25
  204. data/doc/release_notes/5.42.0.txt +0 -136
  205. data/doc/release_notes/5.43.0.txt +0 -98
  206. data/doc/release_notes/5.44.0.txt +0 -32
  207. data/doc/release_notes/5.45.0.txt +0 -34
  208. data/doc/release_notes/5.46.0.txt +0 -87
  209. data/doc/release_notes/5.47.0.txt +0 -59
  210. data/doc/release_notes/5.48.0.txt +0 -14
  211. data/doc/release_notes/5.49.0.txt +0 -59
  212. data/doc/release_notes/5.5.0.txt +0 -61
  213. data/doc/release_notes/5.50.0.txt +0 -78
  214. data/doc/release_notes/5.51.0.txt +0 -47
  215. data/doc/release_notes/5.52.0.txt +0 -87
  216. data/doc/release_notes/5.53.0.txt +0 -23
  217. data/doc/release_notes/5.54.0.txt +0 -27
  218. data/doc/release_notes/5.55.0.txt +0 -21
  219. data/doc/release_notes/5.56.0.txt +0 -51
  220. data/doc/release_notes/5.57.0.txt +0 -23
  221. data/doc/release_notes/5.58.0.txt +0 -31
  222. data/doc/release_notes/5.59.0.txt +0 -73
  223. data/doc/release_notes/5.6.0.txt +0 -31
  224. data/doc/release_notes/5.60.0.txt +0 -22
  225. data/doc/release_notes/5.61.0.txt +0 -43
  226. data/doc/release_notes/5.62.0.txt +0 -132
  227. data/doc/release_notes/5.63.0.txt +0 -33
  228. data/doc/release_notes/5.64.0.txt +0 -50
  229. data/doc/release_notes/5.65.0.txt +0 -21
  230. data/doc/release_notes/5.66.0.txt +0 -24
  231. data/doc/release_notes/5.67.0.txt +0 -32
  232. data/doc/release_notes/5.68.0.txt +0 -61
  233. data/doc/release_notes/5.69.0.txt +0 -26
  234. data/doc/release_notes/5.7.0.txt +0 -108
  235. data/doc/release_notes/5.70.0.txt +0 -35
  236. data/doc/release_notes/5.71.0.txt +0 -21
  237. data/doc/release_notes/5.72.0.txt +0 -33
  238. data/doc/release_notes/5.73.0.txt +0 -66
  239. data/doc/release_notes/5.74.0.txt +0 -45
  240. data/doc/release_notes/5.75.0.txt +0 -35
  241. data/doc/release_notes/5.76.0.txt +0 -86
  242. data/doc/release_notes/5.77.0.txt +0 -63
  243. data/doc/release_notes/5.78.0.txt +0 -67
  244. data/doc/release_notes/5.79.0.txt +0 -28
  245. data/doc/release_notes/5.8.0.txt +0 -170
  246. data/doc/release_notes/5.80.0.txt +0 -40
  247. data/doc/release_notes/5.81.0.txt +0 -31
  248. data/doc/release_notes/5.82.0.txt +0 -61
  249. data/doc/release_notes/5.83.0.txt +0 -56
  250. data/doc/release_notes/5.9.0.txt +0 -99
  251. data/doc/schema_modification.rdoc +0 -679
  252. data/doc/security.rdoc +0 -443
  253. data/doc/sharding.rdoc +0 -286
  254. data/doc/sql.rdoc +0 -648
  255. data/doc/testing.rdoc +0 -204
  256. data/doc/thread_safety.rdoc +0 -15
  257. data/doc/transactions.rdoc +0 -250
  258. data/doc/validations.rdoc +0 -558
  259. data/doc/virtual_rows.rdoc +0 -265
@@ -48,7 +48,7 @@ module Sequel
48
48
  TYPE_TRANSLATOR_DATE = date.freeze
49
49
  bytea = Object.new
50
50
  def bytea.call(str)
51
- str = if str =~ /\A\\x/
51
+ str = if str.start_with?('\\x')
52
52
  # PostgreSQL 9.0+ bytea hex format
53
53
  str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
54
54
  else
@@ -101,7 +101,7 @@ module Sequel
101
101
 
102
102
  def self.mock_adapter_setup(db)
103
103
  db.instance_exec do
104
- @server_version = 150000
104
+ @server_version = 170000
105
105
  initialize_postgres_adapter
106
106
  extend(MockAdapterDatabaseMethods)
107
107
  end
@@ -124,6 +124,8 @@ module Sequel
124
124
  #
125
125
  # Options supported:
126
126
  #
127
+ # :include :: Include additional columns in the underlying index, to
128
+ # allow for index-only scans in more cases (PostgreSQL 11+).
127
129
  # :name :: Name the constraint with the given name (useful if you may
128
130
  # need to drop the constraint later)
129
131
  # :using :: Override the index_method for the exclusion constraint (defaults to gist).
@@ -141,6 +143,22 @@ module Sequel
141
143
  @operations << {:op => :add_constraint, :type => :exclude, :elements => elements}.merge!(opts)
142
144
  end
143
145
 
146
+ # Alter an existing constraint. Options:
147
+ # :deferrable :: Modify deferrable setting for constraint (PostgreSQL 9.4+):
148
+ # true :: DEFERRABLE INITIALLY DEFERRED
149
+ # false :: NOT DEFERRABLE
150
+ # :immediate :: DEFERRABLE INITIALLY IMMEDIATE
151
+ # :enforced :: Set true to use ENFORCED, or false to use NOT ENFORCED (PostgreSQL 18+)
152
+ # :inherit :: Set true to use INHERIT, or false to use NO INHERIT (PostgreSQL 18+)
153
+ def alter_constraint(name, opts=OPTS)
154
+ @operations << {:op => :alter_constraint, :name => name}.merge!(opts)
155
+ end
156
+
157
+ # :inherit :: Set true to use INHERIT, or false to use NO INHERIT (PostgreSQL 18+)
158
+ def rename_constraint(name, new_name)
159
+ @operations << {:op => :rename_constraint, :name => name, :new_name => new_name}
160
+ end
161
+
144
162
  # Validate the constraint with the given name, which should have
145
163
  # been added previously with NOT VALID.
146
164
  def validate_constraint(name)
@@ -321,7 +339,7 @@ module Sequel
321
339
  end
322
340
 
323
341
  def commit_prepared_transaction(transaction_id, opts=OPTS)
324
- run("COMMIT PREPARED #{literal(transaction_id)}", opts)
342
+ run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
325
343
  end
326
344
 
327
345
  # A hash of metadata for CHECK constraints on the table.
@@ -335,7 +353,7 @@ module Sequel
335
353
  hash = {}
336
354
  _check_constraints_ds.where_each(:conrelid=>regclass_oid(table)) do |row|
337
355
  constraint = m.call(row[:constraint])
338
- entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
356
+ entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[], :validated=>row[:validated], :enforced=>row[:enforced]}
339
357
  entry[:columns] << m.call(row[:column]) if row[:column]
340
358
  end
341
359
 
@@ -398,7 +416,7 @@ module Sequel
398
416
  return if already_identity
399
417
 
400
418
  transaction(server_hash) do
401
- run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
419
+ run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
402
420
 
403
421
  ds.from(:pg_depend).
404
422
  where(:classid=>pg_class, :objid=>seq_oid, :objsubid=>0, :deptype=>'a').
@@ -435,7 +453,7 @@ module Sequel
435
453
  # often used here if :security_definer is used.
436
454
  # :strict :: Makes the function return NULL when any argument is NULL.
437
455
  def create_function(name, definition, opts=OPTS)
438
- self << create_function_sql(name, definition, opts)
456
+ self << create_function_sql(name, definition, opts).freeze
439
457
  end
440
458
 
441
459
  # Create the procedural language in the database. Arguments:
@@ -446,7 +464,7 @@ module Sequel
446
464
  # :trusted :: Marks the language being created as trusted, allowing unprivileged users to create functions using this language.
447
465
  # :validator :: The name of previously registered function used as a validator of functions defined in this language.
448
466
  def create_language(name, opts=OPTS)
449
- self << create_language_sql(name, opts)
467
+ self << create_language_sql(name, opts).freeze
450
468
  end
451
469
 
452
470
  # Create a schema in the database. Arguments:
@@ -455,7 +473,7 @@ module Sequel
455
473
  # :if_not_exists :: Don't raise an error if the schema already exists (PostgreSQL 9.3+)
456
474
  # :owner :: The owner to set for the schema (defaults to current user if not specified)
457
475
  def create_schema(name, opts=OPTS)
458
- self << create_schema_sql(name, opts)
476
+ self << create_schema_sql(name, opts).freeze
459
477
  end
460
478
 
461
479
  # Support partitions of tables using the :partition_of option.
@@ -491,7 +509,7 @@ module Sequel
491
509
  # :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
492
510
  # :when :: A filter to use for the trigger
493
511
  def create_trigger(table, name, function, opts=OPTS)
494
- self << create_trigger_sql(table, name, function, opts)
512
+ self << create_trigger_sql(table, name, function, opts).freeze
495
513
  end
496
514
 
497
515
  def database_type
@@ -524,7 +542,7 @@ module Sequel
524
542
  # default is plpgsql. Can be specified as a string or a symbol.
525
543
  def do(code, opts=OPTS)
526
544
  language = opts[:language]
527
- run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
545
+ run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
528
546
  end
529
547
 
530
548
  # Drops the function from the database. Arguments:
@@ -534,7 +552,7 @@ module Sequel
534
552
  # :cascade :: Drop other objects depending on this function.
535
553
  # :if_exists :: Don't raise an error if the function doesn't exist.
536
554
  def drop_function(name, opts=OPTS)
537
- self << drop_function_sql(name, opts)
555
+ self << drop_function_sql(name, opts).freeze
538
556
  end
539
557
 
540
558
  # Drops a procedural language from the database. Arguments:
@@ -543,7 +561,7 @@ module Sequel
543
561
  # :cascade :: Drop other objects depending on this function.
544
562
  # :if_exists :: Don't raise an error if the function doesn't exist.
545
563
  def drop_language(name, opts=OPTS)
546
- self << drop_language_sql(name, opts)
564
+ self << drop_language_sql(name, opts).freeze
547
565
  end
548
566
 
549
567
  # Drops a schema from the database. Arguments:
@@ -552,7 +570,8 @@ module Sequel
552
570
  # :cascade :: Drop all objects in this schema.
553
571
  # :if_exists :: Don't raise an error if the schema doesn't exist.
554
572
  def drop_schema(name, opts=OPTS)
555
- self << drop_schema_sql(name, opts)
573
+ self << drop_schema_sql(name, opts).freeze
574
+ remove_all_cached_schemas
556
575
  end
557
576
 
558
577
  # Drops a trigger from the database. Arguments:
@@ -562,7 +581,7 @@ module Sequel
562
581
  # :cascade :: Drop other objects depending on this function.
563
582
  # :if_exists :: Don't raise an error if the function doesn't exist.
564
583
  def drop_trigger(table, name, opts=OPTS)
565
- self << drop_trigger_sql(table, name, opts)
584
+ self << drop_trigger_sql(table, name, opts).freeze
566
585
  end
567
586
 
568
587
  # Return full foreign key information using the pg system tables, including
@@ -601,6 +620,8 @@ module Sequel
601
620
  :on_update=>fklod_map[row[:on_update]],
602
621
  :on_delete=>fklod_map[row[:on_delete]],
603
622
  :deferrable=>row[:deferrable],
623
+ :validated=>row[:validated],
624
+ :enforced=>row[:enforced],
604
625
  :table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
605
626
  }
606
627
 
@@ -648,12 +669,26 @@ module Sequel
648
669
  _set_constraints(' IMMEDIATE', opts)
649
670
  end
650
671
 
651
- # Use the pg_* system tables to determine indexes on a table
672
+ # Use the pg_* system tables to determine indexes on a table. Options:
673
+ #
674
+ # :include_partial :: Set to true to include partial indexes
675
+ # :invalid :: Set to true or :only to only return invalid indexes.
676
+ # Set to :include to also return both valid and invalid indexes.
677
+ # When not set or other value given, does not return invalid indexes.
652
678
  def indexes(table, opts=OPTS)
653
679
  m = output_identifier_meth
654
680
  cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
655
681
  cond[:indpred] = nil unless opts[:include_partial]
656
682
 
683
+ case opts[:invalid]
684
+ when true, :only
685
+ cond[:indisvalid] = false
686
+ when :include
687
+ # nothing
688
+ else
689
+ cond[:indisvalid] = true
690
+ end
691
+
657
692
  indexes = {}
658
693
  _indexes_ds.where_each(cond) do |r|
659
694
  i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
@@ -706,6 +741,14 @@ module Sequel
706
741
  Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
707
742
  end
708
743
 
744
+ # Rename a schema in the database. Arguments:
745
+ # name :: Current name of the schema
746
+ # opts :: New name for the schema
747
+ def rename_schema(name, new_name)
748
+ self << rename_schema_sql(name, new_name).freeze
749
+ remove_all_cached_schemas
750
+ end
751
+
709
752
  # Refresh the materialized view with the given name.
710
753
  #
711
754
  # DB.refresh_view(:items_view)
@@ -713,7 +756,7 @@ module Sequel
713
756
  # DB.refresh_view(:items_view, concurrently: true)
714
757
  # # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
715
758
  def refresh_view(name, opts=OPTS)
716
- run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
759
+ run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
717
760
  end
718
761
 
719
762
  # Reset the primary key sequence for the given table, basing it on the
@@ -726,7 +769,7 @@ module Sequel
726
769
  table = Sequel.qualify(s, t) if s
727
770
 
728
771
  if server_version >= 100000
729
- seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
772
+ seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
730
773
  increment_by = :seqincrement
731
774
  min_value = :seqmin
732
775
  # :nocov:
@@ -741,7 +784,7 @@ module Sequel
741
784
  end
742
785
 
743
786
  def rollback_prepared_transaction(transaction_id, opts=OPTS)
744
- run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
787
+ run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
745
788
  end
746
789
 
747
790
  # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
@@ -898,11 +941,15 @@ module Sequel
898
941
 
899
942
  # Dataset used to retrieve CHECK constraint information
900
943
  def _check_constraints_ds
901
- @_check_constraints_ds ||= metadata_dataset.
902
- from{pg_constraint.as(:co)}.
903
- left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
904
- where(:contype=>'c').
905
- select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
944
+ @_check_constraints_ds ||= begin
945
+ ds = metadata_dataset.
946
+ from{pg_constraint.as(:co)}.
947
+ left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
948
+ where(:contype=>'c').
949
+ select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
950
+
951
+ _add_validated_enforced_constraint_columns(ds)
952
+ end
906
953
  end
907
954
 
908
955
  # Dataset used to retrieve foreign keys referenced by a table
@@ -968,6 +1015,28 @@ module Sequel
968
1015
  ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
969
1016
  end
970
1017
 
1018
+ _add_validated_enforced_constraint_columns(ds)
1019
+ end
1020
+
1021
+ def _add_validated_enforced_constraint_columns(ds)
1022
+ validated_cond = if server_version >= 90100
1023
+ Sequel[:convalidated]
1024
+ # :nocov:
1025
+ else
1026
+ Sequel.cast(true, TrueClass)
1027
+ # :nocov:
1028
+ end
1029
+ ds = ds.select_append(validated_cond.as(:validated))
1030
+
1031
+ enforced_cond = if server_version >= 180000
1032
+ Sequel[:conenforced]
1033
+ # :nocov:
1034
+ else
1035
+ Sequel.cast(true, TrueClass)
1036
+ # :nocov:
1037
+ end
1038
+ ds = ds.select_append(enforced_cond.as(:enforced))
1039
+
971
1040
  ds
972
1041
  end
973
1042
 
@@ -994,8 +1063,7 @@ module Sequel
994
1063
  where{{
995
1064
  indc[:relkind]=>%w'i I',
996
1065
  ind[:indisprimary]=>false,
997
- :indexprs=>nil,
998
- :indisvalid=>true}}.
1066
+ :indexprs=>nil}}.
999
1067
  order(*order).
1000
1068
  select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
1001
1069
 
@@ -1075,6 +1143,7 @@ module Sequel
1075
1143
  pg_attribute[:attname].as(:name),
1076
1144
  SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1077
1145
  SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1146
+ SQL::Function.new(:col_description, pg_class[:oid], pg_attribute[:attnum]).as(:comment),
1078
1147
  SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1079
1148
  SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1080
1149
  SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
@@ -1139,10 +1208,39 @@ module Sequel
1139
1208
  "ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
1140
1209
  end
1141
1210
 
1211
+ def alter_table_alter_constraint_sql(table, op)
1212
+ sql = String.new
1213
+ sql << "ALTER CONSTRAINT #{quote_identifier(op[:name])}"
1214
+
1215
+ constraint_deferrable_sql_append(sql, op[:deferrable])
1216
+
1217
+ case op[:enforced]
1218
+ when nil
1219
+ when false
1220
+ sql << " NOT ENFORCED"
1221
+ else
1222
+ sql << " ENFORCED"
1223
+ end
1224
+
1225
+ case op[:inherit]
1226
+ when nil
1227
+ when false
1228
+ sql << " NO INHERIT"
1229
+ else
1230
+ sql << " INHERIT"
1231
+ end
1232
+
1233
+ sql
1234
+ end
1235
+
1142
1236
  def alter_table_generator_class
1143
1237
  Postgres::AlterTableGenerator
1144
1238
  end
1145
1239
 
1240
+ def alter_table_rename_constraint_sql(table, op)
1241
+ "RENAME CONSTRAINT #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
1242
+ end
1243
+
1146
1244
  def alter_table_set_column_type_sql(table, op)
1147
1245
  s = super
1148
1246
  if using = op[:using]
@@ -1189,6 +1287,22 @@ module Sequel
1189
1287
  end
1190
1288
  end
1191
1289
 
1290
+ def column_definition_append_include_sql(sql, constraint)
1291
+ if include_cols = constraint[:include]
1292
+ sql << " INCLUDE " << literal(Array(include_cols))
1293
+ end
1294
+ end
1295
+
1296
+ def column_definition_append_primary_key_sql(sql, constraint)
1297
+ super
1298
+ column_definition_append_include_sql(sql, constraint)
1299
+ end
1300
+
1301
+ def column_definition_append_unique_sql(sql, constraint)
1302
+ super
1303
+ column_definition_append_include_sql(sql, constraint)
1304
+ end
1305
+
1192
1306
  # Literalize non-String collate options. This is because unquoted collatations
1193
1307
  # are folded to lowercase, and PostgreSQL used mixed case or capitalized collations.
1194
1308
  def column_definition_collate_sql(sql, column)
@@ -1208,7 +1322,7 @@ module Sequel
1208
1322
  sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
1209
1323
  sql << " AS IDENTITY"
1210
1324
  elsif (generated = column[:generated_always_as])
1211
- sql << " GENERATED ALWAYS AS (#{literal(generated)}) STORED"
1325
+ sql << " GENERATED ALWAYS AS (#{literal(generated)}) #{column[:virtual] ? 'VIRTUAL' : 'STORED'}"
1212
1326
  end
1213
1327
  end
1214
1328
  end
@@ -1232,9 +1346,9 @@ module Sequel
1232
1346
  end
1233
1347
 
1234
1348
  # PostgreSQL can't combine rename_column operations, and it can combine
1235
- # the custom validate_constraint operation.
1349
+ # validate_constraint and alter_constraint operations.
1236
1350
  def combinable_alter_table_op?(op)
1237
- (super || op[:op] == :validate_constraint) && op[:op] != :rename_column
1351
+ (super || op[:op] == :validate_constraint || op[:op] == :alter_constraint) && op[:op] != :rename_column
1238
1352
  end
1239
1353
 
1240
1354
  VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
@@ -1268,26 +1382,97 @@ module Sequel
1268
1382
  sqls
1269
1383
  end
1270
1384
 
1271
- # Handle exclusion constraints.
1385
+ # Handle PostgreSQL-specific constraint features.
1272
1386
  def constraint_definition_sql(constraint)
1273
- case constraint[:type]
1387
+ case type = constraint[:type]
1274
1388
  when :exclude
1275
1389
  elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
1276
1390
  sql = String.new
1277
- sql << "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
1391
+ sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
1392
+ sql << "EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})"
1393
+ column_definition_append_include_sql(sql, constraint)
1394
+ sql << " WHERE #{filter_expr(constraint[:where])}" if constraint[:where]
1395
+ constraint_deferrable_sql_append(sql, constraint[:deferrable])
1396
+ sql
1397
+ when :primary_key, :unique
1398
+ sql = String.new
1399
+ sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
1400
+
1401
+ if type == :primary_key
1402
+ sql << primary_key_constraint_sql_fragment(constraint)
1403
+ else
1404
+ sql << unique_constraint_sql_fragment(constraint)
1405
+ end
1406
+
1407
+ if using_index = constraint[:using_index]
1408
+ sql << " USING INDEX " << quote_identifier(using_index)
1409
+ else
1410
+ cols = literal(constraint[:columns])
1411
+ cols.insert(-2, " WITHOUT OVERLAPS") if constraint[:without_overlaps]
1412
+ sql << " " << cols
1413
+
1414
+ if include_cols = constraint[:include]
1415
+ sql << " INCLUDE " << literal(Array(include_cols))
1416
+ end
1417
+ end
1418
+
1278
1419
  constraint_deferrable_sql_append(sql, constraint[:deferrable])
1279
1420
  sql
1280
- when :foreign_key, :check
1421
+ else # when :foreign_key, :check
1281
1422
  sql = super
1423
+ if constraint[:no_inherit]
1424
+ sql << " NO INHERIT"
1425
+ end
1426
+ if constraint[:not_enforced]
1427
+ sql << " NOT ENFORCED"
1428
+ end
1282
1429
  if constraint[:not_valid]
1283
1430
  sql << " NOT VALID"
1284
1431
  end
1285
1432
  sql
1286
- else
1287
- super
1288
1433
  end
1289
1434
  end
1290
1435
 
1436
+ def column_definition_add_references_sql(sql, column)
1437
+ super
1438
+ if column[:not_enforced]
1439
+ sql << " NOT ENFORCED"
1440
+ end
1441
+ end
1442
+
1443
+ def column_definition_null_sql(sql, column)
1444
+ constraint = column[:not_null]
1445
+ constraint = nil unless constraint.is_a?(Hash)
1446
+ if constraint && (name = constraint[:name])
1447
+ sql << " CONSTRAINT #{quote_identifier(name)}"
1448
+ end
1449
+ super
1450
+ if constraint && constraint[:no_inherit]
1451
+ sql << " NO INHERIT"
1452
+ end
1453
+ end
1454
+
1455
+ # Handle :period option
1456
+ def column_references_table_constraint_sql(constraint)
1457
+ sql = String.new
1458
+ sql << "FOREIGN KEY "
1459
+ cols = constraint[:columns]
1460
+ cols = column_references_add_period(cols) if constraint[:period]
1461
+ sql << literal(cols) << column_references_sql(constraint)
1462
+ end
1463
+
1464
+ def column_references_append_key_sql(sql, column)
1465
+ cols = Array(column[:key])
1466
+ cols = column_references_add_period(cols) if column[:period]
1467
+ sql << "(#{cols.map{|x| quote_identifier(x)}.join(', ')})"
1468
+ end
1469
+
1470
+ def column_references_add_period(cols)
1471
+ cols= cols.dup
1472
+ cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
1473
+ cols
1474
+ end
1475
+
1291
1476
  def database_specific_error_class_from_sqlstate(sqlstate)
1292
1477
  if sqlstate == '23P01'
1293
1478
  ExclusionConstraintViolation
@@ -1357,7 +1542,8 @@ module Sequel
1357
1542
  # SQL statement to create database function.
1358
1543
  def create_function_sql(name, definition, opts=OPTS)
1359
1544
  args = opts[:args]
1360
- if !opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 and %w'OUT INOUT'.include?(a[2].to_s)}
1545
+ in_out = %w'OUT INOUT'
1546
+ if (!opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 && in_out.include?(a[2].to_s)})
1361
1547
  returns = opts[:returns] || 'void'
1362
1548
  end
1363
1549
  language = opts[:language] || 'SQL'
@@ -1584,7 +1770,7 @@ module Sequel
1584
1770
  index_type = :gist
1585
1771
  end
1586
1772
 
1587
- "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
1773
+ "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON#{' ONLY' if index[:only]} #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
1588
1774
  end
1589
1775
 
1590
1776
  # Setup datastructures shared by all postgres adapters.
@@ -1642,6 +1828,18 @@ module Sequel
1642
1828
  super
1643
1829
  end
1644
1830
 
1831
+ # Clear all cached schema information
1832
+ def remove_all_cached_schemas
1833
+ @primary_keys.clear
1834
+ @primary_key_sequences.clear
1835
+ @schemas.clear
1836
+ end
1837
+
1838
+ # SQL for renaming a schema.
1839
+ def rename_schema_sql(name, new_name)
1840
+ "ALTER SCHEMA #{quote_identifier(name)} RENAME TO #{quote_identifier(new_name)}"
1841
+ end
1842
+
1645
1843
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
1646
1844
  # a rename table operation, so specifying a new schema in new_name will not have an effect.
1647
1845
  def rename_table_sql(name, new_name)
@@ -1651,9 +1849,9 @@ module Sequel
1651
1849
  # Handle interval and citext types.
1652
1850
  def schema_column_type(db_type)
1653
1851
  case db_type
1654
- when /\Ainterval\z/io
1852
+ when /\Ainterval\z/i
1655
1853
  :interval
1656
- when /\Acitext\z/io
1854
+ when /\Acitext\z/i
1657
1855
  :string
1658
1856
  else
1659
1857
  super
@@ -1804,6 +2002,15 @@ module Sequel
1804
2002
  end
1805
2003
  end
1806
2004
 
2005
+ # Support :nulls_not_distinct option.
2006
+ def unique_constraint_sql_fragment(constraint)
2007
+ if constraint[:nulls_not_distinct]
2008
+ 'UNIQUE NULLS NOT DISTINCT'
2009
+ else
2010
+ 'UNIQUE'
2011
+ end
2012
+ end
2013
+
1807
2014
  # PostgreSQL 9.4+ supports views with check option.
1808
2015
  def view_with_check_option_support
1809
2016
  # :nocov:
@@ -1876,14 +2083,86 @@ module Sequel
1876
2083
  super
1877
2084
  end
1878
2085
 
1879
- # Return the results of an EXPLAIN query as a string
2086
+ # Return the results of an EXPLAIN query. Boolean options:
2087
+ #
2088
+ # :analyze :: Use the ANALYZE option.
2089
+ # :buffers :: Use the BUFFERS option.
2090
+ # :costs :: Use the COSTS option.
2091
+ # :generic_plan :: Use the GENERIC_PLAN option.
2092
+ # :memory :: Use the MEMORY option.
2093
+ # :settings :: Use the SETTINGS option.
2094
+ # :summary :: Use the SUMMARY option.
2095
+ # :timing :: Use the TIMING option.
2096
+ # :verbose :: Use the VERBOSE option.
2097
+ # :wal :: Use the WAL option.
2098
+ #
2099
+ # Non boolean options:
2100
+ #
2101
+ # :format :: Use the FORMAT option to change the format of the
2102
+ # returned value. Values can be :text, :xml, :json,
2103
+ # or :yaml.
2104
+ # :serialize :: Use the SERIALIZE option to get timing on
2105
+ # serialization. Values can be :none, :text, or
2106
+ # :binary.
2107
+ #
2108
+ # See the PostgreSQL EXPLAIN documentation for an explanation of
2109
+ # what each option does.
2110
+ #
2111
+ # In most cases, the return value is a single string. However,
2112
+ # using the <tt>format: :json</tt> option can result in the return
2113
+ # value being an array containing a hash.
1880
2114
  def explain(opts=OPTS)
1881
- with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
2115
+ rows = clone(:append_sql=>explain_sql_string_origin(opts)).map(:'QUERY PLAN')
2116
+
2117
+ if rows.length == 1
2118
+ rows[0]
2119
+ elsif rows.all?{|row| String === row}
2120
+ rows.join("\r\n")
2121
+ # :nocov:
2122
+ else
2123
+ # This branch is unreachable in tests, but it seems better to just return
2124
+ # all rows than throw in error if this case actually happens.
2125
+ rows
2126
+ # :nocov:
2127
+ end
2128
+ end
2129
+
2130
+ # Return a cloned dataset which will use FOR KEY SHARE to lock returned rows.
2131
+ # Supported on PostgreSQL 9.3+.
2132
+ def for_key_share
2133
+ cached_lock_style_dataset(:_for_key_share_ds, :key_share)
2134
+ end
2135
+
2136
+ # Set FOR PORTION OF clause for UPDATE and DELETE statements.
2137
+ # The first argument is the range or multirange column. If two arguments
2138
+ # are provided, the second argument is an expression with the same
2139
+ # database type as the first argument. If three arguments are provided,
2140
+ # the second specifies the inclusive start of the portion to update and the third
2141
+ # specifies the exclusive end of portion to update. When using the three argument
2142
+ # form, nil can be provided as the second or third argument to have the start or
2143
+ # end of the portion be unbounded. Supported on PostgreSQL 19+.
2144
+ # Example:
2145
+ #
2146
+ # DB[:t].for_portion_of(:rc, Sequel.function(:int4range, 1, 2)).update(c: 3)
2147
+ # # UPDATE t FOR PORTION OF rc (int4range(1, 2)) SET c = 3
2148
+ #
2149
+ # DB[:t].for_portion_of(:rc, 1, 2).update(c: 3)
2150
+ # # UPDATE t FOR PORTION OF rc FROM 1 TO 2 SET c = 3
2151
+ def for_portion_of(column, range, to=(arg_not_given=true))
2152
+ range = [range, to].freeze unless arg_not_given
2153
+ clone(:for_portion_of => [column, range].freeze)
2154
+ end
2155
+
2156
+ # Return a cloned dataset which will use FOR NO KEY UPDATE to lock returned rows.
2157
+ # This is generally a better choice than using for_update on PostgreSQL, unless
2158
+ # you will be deleting the row or modifying a key column. Supported on PostgreSQL 9.3+.
2159
+ def for_no_key_update
2160
+ cached_lock_style_dataset(:_for_no_key_update_ds, :no_key_update)
1882
2161
  end
1883
2162
 
1884
2163
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
1885
2164
  def for_share
1886
- lock_style(:share)
2165
+ cached_lock_style_dataset(:_for_share_ds, :share)
1887
2166
  end
1888
2167
 
1889
2168
  # Run a full text search on PostgreSQL. By default, searching for the inclusion
@@ -1914,7 +2193,7 @@ module Sequel
1914
2193
  end
1915
2194
 
1916
2195
  unless opts[:tsquery]
1917
- phrase_terms = terms.is_a?(Array) ? terms.join(' | ') : terms
2196
+ phrase_terms = terms.is_a?(Array) || terms.is_a?(Set) ? Sequel.array_or_set_join(terms, ' | ') : terms
1918
2197
 
1919
2198
  query_func = case to_tsquery = opts[:to_tsquery]
1920
2199
  when :phrase, :plain
@@ -2376,10 +2655,11 @@ module Sequel
2376
2655
  "'%Y-%m-%d %H:%M:%S.%6N%z'"
2377
2656
  end
2378
2657
 
2379
- # Only include the primary table in the main delete clause
2658
+ # Only include the primary table in the main delete clause.
2659
+ # Support FOR PORTION OF.
2380
2660
  def delete_from_sql(sql)
2381
2661
  sql << ' FROM '
2382
- source_list_append(sql, @opts[:from][0..0])
2662
+ table_for_portion_of_sql_append(sql)
2383
2663
  end
2384
2664
 
2385
2665
  # Use USING to specify additional tables in a delete query
@@ -2387,6 +2667,109 @@ module Sequel
2387
2667
  join_from_sql(:USING, sql)
2388
2668
  end
2389
2669
 
2670
+ # Handle column aliases containing data types, useful for selecting from functions
2671
+ # that return the record data type.
2672
+ def derived_column_list_sql_append(sql, column_aliases)
2673
+ c = false
2674
+ comma = ', '
2675
+ column_aliases.each do |a|
2676
+ sql << comma if c
2677
+ if a.is_a?(Array)
2678
+ raise Error, "column aliases specified as arrays must have only 2 elements, the first is alias name and the second is data type" unless a.length == 2
2679
+ a, type = a
2680
+ identifier_append(sql, a)
2681
+ sql << " " << db.cast_type_literal(type).to_s
2682
+ else
2683
+ identifier_append(sql, a)
2684
+ end
2685
+ c ||= true
2686
+ end
2687
+ end
2688
+
2689
+ EXPLAIN_BOOLEAN_OPTIONS = {}
2690
+ %w[analyze verbose costs settings generic_plan buffers wal timing summary memory].each do |str|
2691
+ EXPLAIN_BOOLEAN_OPTIONS[str.to_sym] = str.upcase.freeze
2692
+ end
2693
+ EXPLAIN_BOOLEAN_OPTIONS.freeze
2694
+
2695
+ EXPLAIN_NONBOOLEAN_OPTIONS = {
2696
+ :serialize => {:none=>"SERIALIZE NONE", :text=>"SERIALIZE TEXT", :binary=>"SERIALIZE BINARY"}.freeze,
2697
+ :format => {:text=>"FORMAT TEXT", :xml=>"FORMAT XML", :json=>"FORMAT JSON", :yaml=>"FORMAT YAML"}.freeze
2698
+ }.freeze
2699
+
2700
+ # A mutable string used as the prefix when explaining a query.
2701
+ def explain_sql_string_origin(opts)
2702
+ origin = String.new
2703
+ origin << 'EXPLAIN '
2704
+
2705
+ # :nocov:
2706
+ if server_version < 90000
2707
+ if opts[:analyze]
2708
+ origin << 'ANALYZE '
2709
+ end
2710
+
2711
+ return origin
2712
+ end
2713
+ # :nocov:
2714
+
2715
+ comma = nil
2716
+ paren = "("
2717
+
2718
+ add_opt = lambda do |str, value|
2719
+ origin << paren if paren
2720
+ origin << comma if comma
2721
+ origin << str
2722
+ origin << " FALSE" unless value
2723
+ comma ||= ', '
2724
+ paren &&= nil
2725
+ end
2726
+
2727
+ EXPLAIN_BOOLEAN_OPTIONS.each do |key, str|
2728
+ unless (value = opts[key]).nil?
2729
+ add_opt.call(str, value)
2730
+ end
2731
+ end
2732
+
2733
+ EXPLAIN_NONBOOLEAN_OPTIONS.each do |key, e_opts|
2734
+ if value = opts[key]
2735
+ if str = e_opts[value]
2736
+ add_opt.call(str, true)
2737
+ else
2738
+ raise Sequel::Error, "unrecognized value for Dataset#explain #{key.inspect} option: #{value.inspect}"
2739
+ end
2740
+ end
2741
+ end
2742
+
2743
+ origin << ') ' unless paren
2744
+ origin
2745
+ end
2746
+
2747
+ # Add FOR PORTION OF SQL if the dataset uses it.
2748
+ def table_for_portion_of_sql_append(sql)
2749
+ fpo_column, fpo_range = @opts[:for_portion_of]
2750
+ if fpo_column
2751
+ table, aliaz = split_alias(@opts[:from].first)
2752
+ source_list_append(sql, [table])
2753
+ sql << ' FOR PORTION OF '
2754
+ literal_append(sql, fpo_column)
2755
+
2756
+ if fpo_range.is_a?(Array)
2757
+ fpo_start, fpo_end = fpo_range
2758
+ sql << ' FROM '
2759
+ literal_append(sql, fpo_start)
2760
+ sql << ' TO '
2761
+ literal_append(sql, fpo_end)
2762
+ else
2763
+ sql << ' ('
2764
+ literal_append(sql, fpo_range)
2765
+ sql << ')'
2766
+ end
2767
+ as_sql_append(sql, aliaz) if aliaz
2768
+ else
2769
+ source_list_append(sql, @opts[:from][0..0])
2770
+ end
2771
+ end
2772
+
2390
2773
  # Add ON CONFLICT clause if it should be used
2391
2774
  def insert_conflict_sql(sql)
2392
2775
  if opts = @opts[:insert_conflict]
@@ -2430,6 +2813,9 @@ module Sequel
2430
2813
  # Return the primary key to use for RETURNING in an INSERT statement
2431
2814
  def insert_pk
2432
2815
  (f = opts[:from]) && !f.empty? && (t = f.first)
2816
+
2817
+ t = t.call(self) if t.is_a? Sequel::SQL::DelayedEvaluation
2818
+
2433
2819
  case t
2434
2820
  when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
2435
2821
  if pk = db.primary_key(t)
@@ -2579,8 +2965,13 @@ module Sequel
2579
2965
  # Use SKIP LOCKED if skipping locked rows.
2580
2966
  def select_lock_sql(sql)
2581
2967
  lock = @opts[:lock]
2582
- if lock == :share
2968
+ case lock
2969
+ when :share
2583
2970
  sql << ' FOR SHARE'
2971
+ when :no_key_update
2972
+ sql << ' FOR NO KEY UPDATE'
2973
+ when :key_share
2974
+ sql << ' FOR KEY SHARE'
2584
2975
  else
2585
2976
  super
2586
2977
  end
@@ -2668,10 +3059,10 @@ module Sequel
2668
3059
  join_from_sql(:FROM, sql)
2669
3060
  end
2670
3061
 
2671
- # Only include the primary table in the main update clause
3062
+ # Support FOR PORTION OF.
2672
3063
  def update_table_sql(sql)
2673
3064
  sql << ' '
2674
- source_list_append(sql, @opts[:from][0..0])
3065
+ table_for_portion_of_sql_append(sql)
2675
3066
  end
2676
3067
  end
2677
3068
  end