sequel 5.35.0 → 5.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +68 -0
  3. data/README.rdoc +2 -2
  4. data/doc/cheat_sheet.rdoc +5 -5
  5. data/doc/code_order.rdoc +0 -12
  6. data/doc/fork_safety.rdoc +84 -0
  7. data/doc/model_plugins.rdoc +1 -1
  8. data/doc/opening_databases.rdoc +5 -1
  9. data/doc/postgresql.rdoc +1 -1
  10. data/doc/querying.rdoc +3 -3
  11. data/doc/release_notes/5.36.0.txt +60 -0
  12. data/doc/release_notes/5.37.0.txt +30 -0
  13. data/doc/release_notes/5.38.0.txt +28 -0
  14. data/doc/release_notes/5.39.0.txt +19 -0
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/transactions.rdoc +0 -8
  17. data/doc/validations.rdoc +1 -1
  18. data/lib/sequel/adapters/jdbc.rb +15 -3
  19. data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
  20. data/lib/sequel/adapters/odbc.rb +4 -6
  21. data/lib/sequel/adapters/shared/mssql.rb +35 -5
  22. data/lib/sequel/adapters/shared/oracle.rb +13 -7
  23. data/lib/sequel/adapters/shared/postgres.rb +40 -2
  24. data/lib/sequel/adapters/shared/sqlite.rb +35 -1
  25. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
  26. data/lib/sequel/core.rb +5 -6
  27. data/lib/sequel/database/connecting.rb +0 -1
  28. data/lib/sequel/database/misc.rb +14 -0
  29. data/lib/sequel/database/schema_generator.rb +6 -0
  30. data/lib/sequel/database/schema_methods.rb +16 -6
  31. data/lib/sequel/database/transactions.rb +2 -2
  32. data/lib/sequel/dataset/actions.rb +10 -6
  33. data/lib/sequel/dataset/features.rb +10 -0
  34. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  35. data/lib/sequel/dataset/sql.rb +32 -10
  36. data/lib/sequel/extensions/blank.rb +6 -0
  37. data/lib/sequel/extensions/date_arithmetic.rb +6 -3
  38. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  39. data/lib/sequel/extensions/inflector.rb +6 -0
  40. data/lib/sequel/extensions/migration.rb +10 -1
  41. data/lib/sequel/extensions/pg_array.rb +1 -0
  42. data/lib/sequel/extensions/pg_interval.rb +22 -6
  43. data/lib/sequel/extensions/pg_json_ops.rb +44 -2
  44. data/lib/sequel/extensions/pg_row.rb +1 -0
  45. data/lib/sequel/extensions/pg_row_ops.rb +24 -0
  46. data/lib/sequel/extensions/query.rb +3 -0
  47. data/lib/sequel/extensions/schema_dumper.rb +3 -3
  48. data/lib/sequel/model.rb +1 -1
  49. data/lib/sequel/model/associations.rb +1 -0
  50. data/lib/sequel/model/base.rb +23 -4
  51. data/lib/sequel/model/plugins.rb +6 -0
  52. data/lib/sequel/plugins/association_proxies.rb +3 -0
  53. data/lib/sequel/plugins/class_table_inheritance.rb +0 -5
  54. data/lib/sequel/plugins/composition.rb +5 -1
  55. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  56. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  57. data/lib/sequel/plugins/dirty.rb +44 -0
  58. data/lib/sequel/plugins/nested_attributes.rb +3 -1
  59. data/lib/sequel/plugins/pg_array_associations.rb +4 -0
  60. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
  61. data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
  62. data/lib/sequel/plugins/tree.rb +9 -4
  63. data/lib/sequel/timezones.rb +8 -3
  64. data/lib/sequel/version.rb +1 -1
  65. metadata +33 -21
@@ -240,7 +240,7 @@ module Sequel
240
240
  if supports_create_or_replace_view?
241
241
  options = options.merge(:replace=>true)
242
242
  else
243
- drop_view(name) rescue nil
243
+ swallow_database_error{drop_view(name)}
244
244
  end
245
245
 
246
246
  create_view(name, source, options)
@@ -580,14 +580,14 @@ module Sequel
580
580
  sql << ' NULL'
581
581
  end
582
582
  end
583
-
583
+
584
584
  # Add primary key SQL fragment to column creation SQL.
585
585
  def column_definition_primary_key_sql(sql, column)
586
586
  if column[:primary_key]
587
587
  if name = column[:primary_key_constraint_name]
588
588
  sql << " CONSTRAINT #{quote_identifier(name)}"
589
589
  end
590
- sql << ' PRIMARY KEY'
590
+ sql << " " << primary_key_constraint_sql_fragment(column)
591
591
  constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
592
592
  end
593
593
  end
@@ -608,7 +608,7 @@ module Sequel
608
608
  if name = column[:unique_constraint_name]
609
609
  sql << " CONSTRAINT #{quote_identifier(name)}"
610
610
  end
611
- sql << ' UNIQUE'
611
+ sql << ' ' << unique_constraint_sql_fragment(column)
612
612
  constraint_deferrable_sql_append(sql, column[:unique_deferrable])
613
613
  end
614
614
  end
@@ -656,11 +656,11 @@ module Sequel
656
656
  check = "(#{check})" unless check[0..0] == '(' && check[-1..-1] == ')'
657
657
  sql << "CHECK #{check}"
658
658
  when :primary_key
659
- sql << "PRIMARY KEY #{literal(constraint[:columns])}"
659
+ sql << "#{primary_key_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
660
660
  when :foreign_key
661
661
  sql << column_references_table_constraint_sql(constraint.merge(:deferrable=>nil))
662
662
  when :unique
663
- sql << "UNIQUE #{literal(constraint[:columns])}"
663
+ sql << "#{unique_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
664
664
  else
665
665
  raise Error, "Invalid constraint type #{constraint[:type]}, should be :check, :primary_key, :foreign_key, or :unique"
666
666
  end
@@ -892,6 +892,11 @@ module Sequel
892
892
  on_delete_clause(action)
893
893
  end
894
894
 
895
+ # Add fragment for primary key specification, separated for easier overridding.
896
+ def primary_key_constraint_sql_fragment(_)
897
+ 'PRIMARY KEY'
898
+ end
899
+
895
900
  # Proxy the quote_schema_table method to the dataset
896
901
  def quote_schema_table(table)
897
902
  schema_utility_dataset.quote_schema_table(table)
@@ -1047,6 +1052,11 @@ module Sequel
1047
1052
  "#{type}#{literal(Array(elements)) if elements}#{' UNSIGNED' if column[:unsigned]}"
1048
1053
  end
1049
1054
 
1055
+ # Add fragment for unique specification, separated for easier overridding.
1056
+ def unique_constraint_sql_fragment(_)
1057
+ 'UNIQUE'
1058
+ end
1059
+
1050
1060
  # Whether clob should be used for String text: true columns.
1051
1061
  def uses_clob_for_text?
1052
1062
  false
@@ -82,7 +82,7 @@ module Sequel
82
82
  # :server :: The server/shard the transaction is being executed on.
83
83
  def rollback_on_exit(opts=OPTS)
84
84
  synchronize(opts[:server]) do |conn|
85
- raise Error, "Cannot call Sequel:: Database#rollback_on_exit! unless inside a transaction" unless h = _trans(conn)
85
+ raise Error, "Cannot call Sequel:: Database#rollback_on_exit unless inside a transaction" unless h = _trans(conn)
86
86
  rollback = !opts[:cancel]
87
87
 
88
88
  if supports_savepoints?
@@ -154,7 +154,7 @@ module Sequel
154
154
  # Note that this should not be used unless the entire transaction
155
155
  # block is idempotent, as otherwise it can cause non-idempotent
156
156
  # behavior to execute multiple times.
157
- # :rollback :: Can the set to :reraise to reraise any Sequel::Rollback exceptions
157
+ # :rollback :: Can be set to :reraise to reraise any Sequel::Rollback exceptions
158
158
  # raised, or :always to always rollback even if no exceptions occur
159
159
  # (useful for testing).
160
160
  # :server :: The server to use for the transaction. Set to :default, :read_only, or
@@ -607,14 +607,16 @@ module Sequel
607
607
  # as_hash, it accepts an optional :hash parameter, into which entries will
608
608
  # be merged.
609
609
  #
610
- # DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
610
+ # DB[:table].select_hash(:id, :name)
611
+ # # SELECT id, name FROM table
611
612
  # # => {1=>'a', 2=>'b', ...}
612
613
  #
613
614
  # You can also provide an array of column names for either the key_column,
614
615
  # the value column, or both:
615
616
  #
616
- # DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
617
- # # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
617
+ # DB[:table].select_hash([:id, :foo], [:name, :bar])
618
+ # # SELECT id, foo, name, bar FROM table
619
+ # # => {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
618
620
  #
619
621
  # When using this method, you must be sure that each expression has an alias
620
622
  # that Sequel can determine.
@@ -626,14 +628,16 @@ module Sequel
626
628
  # Similar to to_hash_groups, but only selects the columns given. Like to_hash_groups,
627
629
  # it accepts an optional :hash parameter, into which entries will be merged.
628
630
  #
629
- # DB[:table].select_hash_groups(:name, :id) # SELECT id, name FROM table
631
+ # DB[:table].select_hash_groups(:name, :id)
632
+ # # SELECT id, name FROM table
630
633
  # # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
631
634
  #
632
635
  # You can also provide an array of column names for either the key_column,
633
636
  # the value column, or both:
634
637
  #
635
- # DB[:table].select_hash_groups([:first, :middle], [:last, :id]) # SELECT * FROM table
636
- # # {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
638
+ # DB[:table].select_hash_groups([:first, :middle], [:last, :id])
639
+ # # SELECT first, middle, last, id FROM table
640
+ # # => {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
637
641
  #
638
642
  # When using this method, you must be sure that each expression has an alias
639
643
  # that Sequel can determine.
@@ -51,6 +51,11 @@ module Sequel
51
51
  false
52
52
  end
53
53
 
54
+ # Whether deleting from joined datasets is supported, false by default.
55
+ def supports_deleting_joins?
56
+ supports_modifying_joins?
57
+ end
58
+
54
59
  # Whether the database supports derived column lists (e.g.
55
60
  # "table_expr AS table_alias(column_alias1, column_alias2, ...)"), true by
56
61
  # default.
@@ -178,6 +183,11 @@ module Sequel
178
183
  true
179
184
  end
180
185
 
186
+ # Whether updating joined datasets is supported, false by default.
187
+ def supports_updating_joins?
188
+ supports_modifying_joins?
189
+ end
190
+
181
191
  # Whether the dataset supports the WINDOW clause to define windows used by multiple
182
192
  # window functions, false by default.
183
193
  def supports_window_clause?
@@ -201,7 +201,9 @@ module Sequel
201
201
  when :insert_pk
202
202
  fetch_rows(prepared_sql){|r| return r.values.first}
203
203
  when Array
204
+ # :nocov:
204
205
  case prepared_type[0]
206
+ # :nocov:
205
207
  when :map, :as_hash, :to_hash, :to_hash_groups
206
208
  public_send(*prepared_type, &block)
207
209
  end
@@ -22,7 +22,7 @@ module Sequel
22
22
  def insert_sql(*values)
23
23
  return static_sql(@opts[:sql]) if @opts[:sql]
24
24
 
25
- check_modification_allowed!
25
+ check_insert_allowed!
26
26
 
27
27
  columns = []
28
28
 
@@ -172,7 +172,7 @@ module Sequel
172
172
  # than one table.
173
173
  def update_sql(values = OPTS)
174
174
  return static_sql(opts[:sql]) if opts[:sql]
175
- check_modification_allowed!
175
+ check_update_allowed!
176
176
  check_not_limited!(:update)
177
177
 
178
178
  case values
@@ -215,7 +215,7 @@ module Sequel
215
215
  lines << "def #{'_' if priv}#{type}_sql"
216
216
  lines << 'if sql = opts[:sql]; return static_sql(sql) end' unless priv
217
217
  lines << "if sql = cache_get(:_#{type}_sql); return sql end" if cacheable
218
- lines << 'check_modification_allowed!' << 'check_not_limited!(:delete)' if type == :delete
218
+ lines << 'check_delete_allowed!' << 'check_not_limited!(:delete)' if type == :delete
219
219
  lines << 'sql = @opts[:append_sql] || sql_string_origin'
220
220
 
221
221
  if clauses.all?{|c| c.is_a?(Array)}
@@ -918,10 +918,35 @@ module Sequel
918
918
  !@opts[:no_cache_sql] && !cache_get(:_no_cache_sql)
919
919
  end
920
920
 
921
- # Raise an InvalidOperation exception if deletion is not allowed for this dataset.
921
+ # Raise an InvalidOperation exception if modification is not allowed for this dataset.
922
+ # Check whether it is allowed to insert into this dataset.
923
+ # Only for backwards compatibility with older external adapters.
922
924
  def check_modification_allowed!
925
+ # SEQUEL6: Remove
926
+ Sequel::Deprecation.deprecate("Dataset#check_modification_allowed!", "Use check_{insert,delete,update,truncation}_allowed! instead")
927
+ _check_modification_allowed!(supports_modifying_joins?)
928
+ end
929
+
930
+ # Check whether it is allowed to insert into this dataset.
931
+ def check_insert_allowed!
932
+ _check_modification_allowed!(false)
933
+ end
934
+ alias check_truncation_allowed! check_insert_allowed!
935
+
936
+ # Check whether it is allowed to delete from this dataset.
937
+ def check_delete_allowed!
938
+ _check_modification_allowed!(supports_deleting_joins?)
939
+ end
940
+
941
+ # Check whether it is allowed to update this dataset.
942
+ def check_update_allowed!
943
+ _check_modification_allowed!(supports_updating_joins?)
944
+ end
945
+
946
+ # Internals of the check_*_allowed! methods
947
+ def _check_modification_allowed!(modifying_joins_supported)
923
948
  raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
924
- raise(InvalidOperation, "Joined datasets cannot be modified") if !supports_modifying_joins? && joined_dataset?
949
+ raise(InvalidOperation, "Joined datasets cannot be modified") if !modifying_joins_supported && joined_dataset?
925
950
  end
926
951
 
927
952
  # Raise error if the dataset uses limits or offsets.
@@ -930,11 +955,6 @@ module Sequel
930
955
  raise InvalidOperation, "Dataset##{type} not supported on datasets with limits or offsets" if opts[:limit] || opts[:offset]
931
956
  end
932
957
 
933
- # Alias of check_modification_allowed!
934
- def check_truncation_allowed!
935
- check_modification_allowed!
936
- end
937
-
938
958
  # Append column list to SQL string.
939
959
  # If the column list is empty, a wildcard (*) is appended.
940
960
  def column_list_append(sql, columns)
@@ -971,7 +991,9 @@ module Sequel
971
991
  # operators unsupported by some databases. Used by adapters for databases
972
992
  # that don't support the operators natively.
973
993
  def complex_expression_emulate_append(sql, op, args)
994
+ # :nocov:
974
995
  case op
996
+ # :nocov:
975
997
  when :%
976
998
  complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:MOD, a, b)}
977
999
  when :>>
@@ -6,6 +6,12 @@
6
6
  #
7
7
  # Sequel.extension :blank
8
8
 
9
+ [FalseClass, Object, NilClass, Numeric, String, TrueClass].each do |klass|
10
+ if klass.method_defined?(:blank?)
11
+ klass.send(:alias_method, :blank?, :blank?)
12
+ end
13
+ end
14
+
9
15
  class FalseClass
10
16
  # false is always blank
11
17
  def blank?
@@ -49,7 +49,10 @@ module Sequel
49
49
  # Options:
50
50
  # :cast :: Cast to the specified type instead of the default if casting
51
51
  def date_sub(expr, interval, opts=OPTS)
52
- interval = if interval.is_a?(Hash)
52
+ if defined?(ActiveSupport::Duration) && interval.is_a?(ActiveSupport::Duration)
53
+ interval = interval.parts
54
+ end
55
+ interval = if interval.is_a?(Enumerable)
53
56
  h = {}
54
57
  interval.each{|k,v| h[k] = -v unless v.nil?}
55
58
  h
@@ -113,12 +116,12 @@ module Sequel
113
116
  end
114
117
  when :mssql, :h2, :access, :sqlanywhere
115
118
  units = case db_type
116
- when :mssql, :sqlanywhere
117
- MSSQL_DURATION_UNITS
118
119
  when :h2
119
120
  H2_DURATION_UNITS
120
121
  when :access
121
122
  ACCESS_DURATION_UNITS
123
+ else
124
+ MSSQL_DURATION_UNITS
122
125
  end
123
126
  each_valid_interval_unit(h, units) do |value, sql_unit|
124
127
  expr = Sequel.function(:DATEADD, sql_unit, value, expr)
@@ -55,6 +55,8 @@ module Sequel
55
55
 
56
56
  module SQL
57
57
  class Expression
58
+ alias inspect inspect
59
+
58
60
  # Attempt to produce a string suitable for eval, such that:
59
61
  #
60
62
  # eval(obj.inspect) == obj
@@ -105,6 +105,12 @@ class String
105
105
  yield Inflections if block_given?
106
106
  Inflections
107
107
  end
108
+
109
+ %w'classify constantize dasherize demodulize foreign_key humanize pluralize singularize tableize underscore'.each do |m|
110
+ if method_defined?(m)
111
+ alias_method(m, m)
112
+ end
113
+ end
108
114
 
109
115
  # By default, camelize converts the string to UpperCamelCase. If the argument to camelize
110
116
  # is set to :lower then camelize produces lowerCamelCase.
@@ -68,6 +68,9 @@ module Sequel
68
68
  # Allow calling private methods for backwards compatibility
69
69
  @db.send(method_sym, *args, &block)
70
70
  end
71
+ # :nocov:
72
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
73
+ # :nocov:
71
74
 
72
75
  # This object responds to all methods the database responds to.
73
76
  def respond_to_missing?(meth, include_private)
@@ -329,7 +332,8 @@ module Sequel
329
332
  # schema_migrations for timestamped migrations). in the database to keep track
330
333
  # of the current migration version. If no migration version is stored in the
331
334
  # database, the version is considered to be 0. If no target version is
332
- # specified, the database is migrated to the latest version available in the
335
+ # specified, or the target version specified is greater than the latest
336
+ # version available, the database is migrated to the latest version available in the
333
337
  # migration directory.
334
338
  #
335
339
  # For example, to migrate the database to the latest version:
@@ -537,6 +541,11 @@ module Sequel
537
541
  end
538
542
 
539
543
  @direction = current < target ? :up : :down
544
+
545
+ if @direction == :down && @current >= @files.length
546
+ raise Migrator::Error, "Missing migration version(s) needed to migrate down to target version (current: #{current}, target: #{target})"
547
+ end
548
+
540
549
  @migrations = get_migrations
541
550
  end
542
551
 
@@ -213,6 +213,7 @@ module Sequel
213
213
  scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, type)}"
214
214
  define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)}
215
215
  private meth
216
+ alias_method(meth, meth)
216
217
  end
217
218
 
218
219
  @schema_type_classes[:"#{type}_array"] = PGArray
@@ -34,6 +34,13 @@
34
34
 
35
35
  require 'active_support/duration'
36
36
 
37
+ # :nocov:
38
+ begin
39
+ require 'active_support/version'
40
+ rescue LoadError
41
+ end
42
+ # :nocov:
43
+
37
44
  module Sequel
38
45
  module Postgres
39
46
  module IntervalDatabaseMethods
@@ -61,34 +68,37 @@ module Sequel
61
68
 
62
69
  # Creates callable objects that convert strings into ActiveSupport::Duration instances.
63
70
  class Parser
71
+ # Whether ActiveSupport::Duration.new takes parts as array instead of hash
72
+ USE_PARTS_ARRAY = !defined?(ActiveSupport::VERSION::STRING) || ActiveSupport::VERSION::STRING < '5.1'
73
+
64
74
  # Parse the interval input string into an ActiveSupport::Duration instance.
65
75
  def call(string)
66
76
  raise(InvalidValue, "invalid or unhandled interval format: #{string.inspect}") unless matches = /\A([+-]?\d+ years?\s?)?([+-]?\d+ mons?\s?)?([+-]?\d+ days?\s?)?(?:(?:([+-])?(\d{2,10}):(\d\d):(\d\d(\.\d+)?))|([+-]?\d+ hours?\s?)?([+-]?\d+ mins?\s?)?([+-]?\d+(\.\d+)? secs?\s?)?)?\z/.match(string)
67
77
 
68
78
  value = 0
69
- parts = []
79
+ parts = {}
70
80
 
71
81
  if v = matches[1]
72
82
  v = v.to_i
73
83
  value += 31557600 * v
74
- parts << [:years, v]
84
+ parts[:years] = v
75
85
  end
76
86
  if v = matches[2]
77
87
  v = v.to_i
78
88
  value += 2592000 * v
79
- parts << [:months, v]
89
+ parts[:months] = v
80
90
  end
81
91
  if v = matches[3]
82
92
  v = v.to_i
83
93
  value += 86400 * v
84
- parts << [:days, v]
94
+ parts[:days] = v
85
95
  end
86
96
  if matches[5]
87
97
  seconds = matches[5].to_i * 3600 + matches[6].to_i * 60
88
98
  seconds += matches[8] ? matches[7].to_f : matches[7].to_i
89
99
  seconds *= -1 if matches[4] == '-'
90
100
  value += seconds
91
- parts << [:seconds, seconds]
101
+ parts[:seconds] = seconds
92
102
  elsif matches[9] || matches[10] || matches[11]
93
103
  seconds = 0
94
104
  if v = matches[9]
@@ -101,8 +111,14 @@ module Sequel
101
111
  seconds += matches[12] ? v.to_f : v.to_i
102
112
  end
103
113
  value += seconds
104
- parts << [:seconds, seconds]
114
+ parts[:seconds] = seconds
115
+ end
116
+
117
+ # :nocov:
118
+ if USE_PARTS_ARRAY
119
+ parts = parts.to_a
105
120
  end
121
+ # :nocov:
106
122
 
107
123
  ActiveSupport::Duration.new(value, parts)
108
124
  end
@@ -73,7 +73,10 @@
73
73
  # j.pretty # jsonb_pretty(jsonb_column)
74
74
  # j.set(%w'0 a', :h) # jsonb_set(jsonb_column, ARRAY['0','a'], h, true)
75
75
  #
76
- # On PostgreSQL 12+ SQL/JSON functions and operators are supported:
76
+ # j.set_lax(%w'0 a', :h, false, 'raise_exception')
77
+ # # jsonb_set_lax(jsonb_column, ARRAY['0','a'], h, false, 'raise_exception')
78
+ #
79
+ # On PostgreSQL 12+ SQL/JSON path functions and operators are supported:
77
80
  #
78
81
  # j.path_exists('$.foo') # (jsonb_column @? '$.foo')
79
82
  # j.path_match('$.foo') # (jsonb_column @@ '$.foo')
@@ -84,7 +87,15 @@
84
87
  # j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo')
85
88
  # j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo')
86
89
  #
87
- # For the PostgreSQL 12+ SQL/JSON functions, one argument is required (+path+) and
90
+ # On PostgreSQL 13+ timezone-aware SQL/JSON path functions and operators are supported:
91
+ #
92
+ # j.path_exists_tz!('$.foo') # jsonb_path_exists_tz(jsonb_column, '$.foo')
93
+ # j.path_match_tz!('$.foo') # jsonb_path_match_tz(jsonb_column, '$.foo')
94
+ # j.path_query_tz('$.foo') # jsonb_path_query_tz(jsonb_column, '$.foo')
95
+ # j.path_query_array_tz('$.foo') # jsonb_path_query_array_tz(jsonb_column, '$.foo')
96
+ # j.path_query_first_tz('$.foo') # jsonb_path_query_first_tz(jsonb_column, '$.foo')
97
+ #
98
+ # For the PostgreSQL 12+ SQL/JSON path functions, one argument is required (+path+) and
88
99
  # two more arguments are optional (+vars+ and +silent+). +path+ specifies the JSON path.
89
100
  # +vars+ specifies a hash or a string in JSON format of named variables to be
90
101
  # substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
@@ -402,6 +413,11 @@ module Sequel
402
413
  Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists, path, vars, silent))
403
414
  end
404
415
 
416
+ # The same as #path_exists!, except that timezone-aware conversions are used for date/time values.
417
+ def path_exists_tz!(path, vars=nil, silent=nil)
418
+ Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists_tz, path, vars, silent))
419
+ end
420
+
405
421
  # Returns the first item of the result of JSON path predicate check for the json object.
406
422
  # Returns nil if the first item is not true or false.
407
423
  #
@@ -425,6 +441,11 @@ module Sequel
425
441
  Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match, path, vars, silent))
426
442
  end
427
443
 
444
+ # The same as #path_match!, except that timezone-aware conversions are used for date/time values.
445
+ def path_match_tz!(path, vars=nil, silent=nil)
446
+ Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match_tz, path, vars, silent))
447
+ end
448
+
428
449
  # Returns a set of all jsonb values specified by the JSON path
429
450
  # for the json object.
430
451
  #
@@ -440,6 +461,11 @@ module Sequel
440
461
  _path_function(:jsonb_path_query, path, vars, silent)
441
462
  end
442
463
 
464
+ # The same as #path_query, except that timezone-aware conversions are used for date/time values.
465
+ def path_query_tz(path, vars=nil, silent=nil)
466
+ _path_function(:jsonb_path_query_tz, path, vars, silent)
467
+ end
468
+
443
469
  # Returns a jsonb array of all values specified by the JSON path
444
470
  # for the json object.
445
471
  #
@@ -455,6 +481,11 @@ module Sequel
455
481
  JSONBOp.new(_path_function(:jsonb_path_query_array, path, vars, silent))
456
482
  end
457
483
 
484
+ # The same as #path_query_array, except that timezone-aware conversions are used for date/time values.
485
+ def path_query_array_tz(path, vars=nil, silent=nil)
486
+ JSONBOp.new(_path_function(:jsonb_path_query_array_tz, path, vars, silent))
487
+ end
488
+
458
489
  # Returns the first item of the result specified by the JSON path
459
490
  # for the json object.
460
491
  #
@@ -470,6 +501,11 @@ module Sequel
470
501
  JSONBOp.new(_path_function(:jsonb_path_query_first, path, vars, silent))
471
502
  end
472
503
 
504
+ # The same as #path_query_first, except that timezone-aware conversions are used for date/time values.
505
+ def path_query_first_tz(path, vars=nil, silent=nil)
506
+ JSONBOp.new(_path_function(:jsonb_path_query_first_tz, path, vars, silent))
507
+ end
508
+
473
509
  # Return the receiver, since it is already a JSONBOp.
474
510
  def pg_jsonb
475
511
  self
@@ -492,6 +528,12 @@ module Sequel
492
528
  self.class.new(function(:set, wrap_input_array(path), wrap_input_jsonb(other), create_missing))
493
529
  end
494
530
 
531
+ # The same as #set, except if +other+ is +nil+, then behaves according to +null_value_treatment+,
532
+ # which can be one of 'raise_exception', 'use_json_null' (default), 'delete_key', or 'return_target'.
533
+ def set_lax(path, other, create_missing=true, null_value_treatment='use_json_null')
534
+ self.class.new(function(:set_lax, wrap_input_array(path), wrap_input_jsonb(other), create_missing, null_value_treatment))
535
+ end
536
+
495
537
  private
496
538
 
497
539
  # Internals of the jsonb SQL/JSON path functions.