sequel 5.77.0 → 5.81.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -43,7 +43,7 @@ module Sequel
43
43
  add_graph_aliases distinct except exclude exclude_having
44
44
  filter for_update from from_self graph grep group group_and_count group_append group_by having intersect invert
45
45
  limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
46
- reverse reverse_order select select_all select_append select_group select_more server
46
+ reverse reverse_order select select_all select_append select_group select_more select_prepend server
47
47
  set_graph_aliases unfiltered ungraphed ungrouped union
48
48
  unlimited unordered where with with_recursive with_sql
49
49
  METHS
@@ -129,6 +129,7 @@ module Sequel
129
129
  def distinct(*args, &block)
130
130
  virtual_row_columns(args, block)
131
131
  if args.empty?
132
+ return self if opts[:distinct] == EMPTY_ARRAY
132
133
  cached_dataset(:_distinct_ds){clone(:distinct => EMPTY_ARRAY)}
133
134
  else
134
135
  raise(InvalidOperation, "DISTINCT ON not supported") unless supports_distinct_on?
@@ -230,6 +231,7 @@ module Sequel
230
231
  #
231
232
  # DB[:table].for_update # SELECT * FROM table FOR UPDATE
232
233
  def for_update
234
+ return self if opts[:lock] == :update
233
235
  cached_dataset(:_for_update_ds){lock_style(:update)}
234
236
  end
235
237
 
@@ -641,6 +643,7 @@ module Sequel
641
643
  # DB.from(:a, DB[:b].where(Sequel[:a][:c]=>Sequel[:b][:d]).lateral)
642
644
  # # SELECT * FROM a, LATERAL (SELECT * FROM b WHERE (a.c = b.d))
643
645
  def lateral
646
+ return self if opts[:lateral]
644
647
  cached_dataset(:_lateral_ds){clone(:lateral=>true)}
645
648
  end
646
649
 
@@ -744,6 +747,7 @@ module Sequel
744
747
  # ds.all # => [{2=>:id}]
745
748
  # ds.naked.all # => [{:id=>2}]
746
749
  def naked
750
+ return self unless opts[:row_proc]
747
751
  cached_dataset(:_naked_ds){with_row_proc(nil)}
748
752
  end
749
753
 
@@ -753,6 +757,7 @@ module Sequel
753
757
  # DB[:items].for_update.nowait
754
758
  # # SELECT * FROM items FOR UPDATE NOWAIT
755
759
  def nowait
760
+ return self if opts[:nowait]
756
761
  cached_dataset(:_nowait_ds) do
757
762
  raise(Error, 'This dataset does not support raises errors instead of waiting for locked rows') unless supports_nowait?
758
763
  clone(:nowait=>true)
@@ -878,6 +883,7 @@ module Sequel
878
883
  # end
879
884
  def returning(*values)
880
885
  if values.empty?
886
+ return self if opts[:returning] == EMPTY_ARRAY
881
887
  cached_dataset(:_returning_ds) do
882
888
  raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
883
889
  clone(:returning=>EMPTY_ARRAY)
@@ -930,6 +936,7 @@ module Sequel
930
936
  # DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
931
937
  def select_all(*tables)
932
938
  if tables.empty?
939
+ return self unless opts[:select]
933
940
  cached_dataset(:_select_all_ds){clone(:select => nil)}
934
941
  else
935
942
  select(*tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze)
@@ -944,14 +951,8 @@ module Sequel
944
951
  # DB[:items].select(:a).select_append(:b) # SELECT a, b FROM items
945
952
  # DB[:items].select_append(:b) # SELECT *, b FROM items
946
953
  def select_append(*columns, &block)
947
- cur_sel = @opts[:select]
948
- if !cur_sel || cur_sel.empty?
949
- unless supports_select_all_and_column?
950
- return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_append(*columns, &block)
951
- end
952
- cur_sel = [WILDCARD]
953
- end
954
- select(*(cur_sel + columns), &block)
954
+ virtual_row_columns(columns, block)
955
+ select(*(_current_select(true) + columns))
955
956
  end
956
957
 
957
958
  # Set both the select and group clauses with the given +columns+.
@@ -973,6 +974,18 @@ module Sequel
973
974
  select_append(*columns, &block)
974
975
  end
975
976
 
977
+ # Returns a copy of the dataset with the given columns added
978
+ # to the existing selected columns. If no columns are currently selected,
979
+ # it will select the columns given in addition to *.
980
+ #
981
+ # DB[:items].select(:a).select(:b) # SELECT b FROM items
982
+ # DB[:items].select(:a).select_prepend(:b) # SELECT b, a FROM items
983
+ # DB[:items].select_prepend(:b) # SELECT b, * FROM items
984
+ def select_prepend(*columns, &block)
985
+ virtual_row_columns(columns, block)
986
+ select(*(columns + _current_select(false)))
987
+ end
988
+
976
989
  # Set the server for this dataset to use. Used to pick a specific database
977
990
  # shard to run a query against, or to override the default (where SELECT uses
978
991
  # :read_only database and all other queries use the :default database). This
@@ -999,6 +1012,7 @@ module Sequel
999
1012
 
1000
1013
  # Specify that the check for limits/offsets when updating/deleting be skipped for the dataset.
1001
1014
  def skip_limit_check
1015
+ return self if opts[:skip_limit_check]
1002
1016
  cached_dataset(:_skip_limit_check_ds) do
1003
1017
  clone(:skip_limit_check=>true)
1004
1018
  end
@@ -1006,6 +1020,7 @@ module Sequel
1006
1020
 
1007
1021
  # Skip locked rows when returning results from this dataset.
1008
1022
  def skip_locked
1023
+ return self if opts[:skip_locked]
1009
1024
  cached_dataset(:_skip_locked_ds) do
1010
1025
  raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
1011
1026
  clone(:skip_locked=>true)
@@ -1017,6 +1032,7 @@ module Sequel
1017
1032
  # DB[:items].group(:a).having(a: 1).where(:b).unfiltered
1018
1033
  # # SELECT * FROM items GROUP BY a
1019
1034
  def unfiltered
1035
+ return self unless opts[:where] || opts[:having]
1020
1036
  cached_dataset(:_unfiltered_ds){clone(:where => nil, :having => nil)}
1021
1037
  end
1022
1038
 
@@ -1025,6 +1041,7 @@ module Sequel
1025
1041
  # DB[:items].group(:a).having(a: 1).where(:b).ungrouped
1026
1042
  # # SELECT * FROM items WHERE b
1027
1043
  def ungrouped
1044
+ return self unless opts[:group] || opts[:having]
1028
1045
  cached_dataset(:_ungrouped_ds){clone(:group => nil, :having => nil)}
1029
1046
  end
1030
1047
 
@@ -1052,6 +1069,7 @@ module Sequel
1052
1069
  #
1053
1070
  # DB[:items].limit(10, 20).unlimited # SELECT * FROM items
1054
1071
  def unlimited
1072
+ return self unless opts[:limit] || opts[:offset]
1055
1073
  cached_dataset(:_unlimited_ds){clone(:limit=>nil, :offset=>nil)}
1056
1074
  end
1057
1075
 
@@ -1059,6 +1077,7 @@ module Sequel
1059
1077
  #
1060
1078
  # DB[:items].order(:a).unordered # SELECT * FROM items
1061
1079
  def unordered
1080
+ return self unless opts[:order]
1062
1081
  cached_dataset(:_unordered_ds){clone(:order=>nil)}
1063
1082
  end
1064
1083
 
@@ -1353,6 +1372,36 @@ module Sequel
1353
1372
  end
1354
1373
  # :nocov:
1355
1374
 
1375
+ # A frozen array for the currently selected columns.
1376
+ def _current_select(allow_plain_wildcard)
1377
+ cur_sel = @opts[:select]
1378
+
1379
+ if !cur_sel || cur_sel.empty?
1380
+ cur_sel = if allow_plain_wildcard && supports_select_all_and_column?
1381
+ [WILDCARD].freeze
1382
+ else
1383
+ _current_select_column_all
1384
+ end
1385
+ elsif !allow_plain_wildcard && cur_sel.include?(WILDCARD)
1386
+ cur_sel = cur_sel.dup
1387
+ index = cur_sel.index(WILDCARD)
1388
+ cur_sel.delete(WILDCARD)
1389
+ _current_select_column_all.each_with_index do |ca, i|
1390
+ cur_sel.insert(index+i, ca)
1391
+ end
1392
+ cur_sel.freeze
1393
+ end
1394
+
1395
+ cur_sel
1396
+ end
1397
+
1398
+ # An array of SQL::ColumnAll objects for all FROM and JOIN tables. Used for select_append
1399
+ # and select_prepend.
1400
+ def _current_select_column_all
1401
+ tables = Array(@opts[:from]) + Array(@opts[:join])
1402
+ tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze
1403
+ end
1404
+
1356
1405
  # If invert is true, invert the condition.
1357
1406
  def _invert_filter(cond, invert)
1358
1407
  if invert
@@ -18,6 +18,11 @@ module Sequel
18
18
  end
19
19
  end
20
20
  end
21
+
22
+ (
23
+ # Error raised when there is a failed attempt to acquire an advisory lock.
24
+ AdvisoryLockError = Class.new(Error)
25
+ ).name
21
26
 
22
27
  (
23
28
  # Error raised when the adapter requested doesn't exist or can't be loaded.
@@ -176,6 +176,13 @@
176
176
  # +:preempt_async_thread+ Database option before loading the
177
177
  # async_thread_pool extension.
178
178
  #
179
+ # Note that the async_thread_pool extension creates the thread pool
180
+ # when it is loaded into the Database. If you fork after loading
181
+ # the extension, the extension will not work, as fork does not
182
+ # copy the thread pools. If you are using a forking webserver
183
+ # (or any other system that forks worker processes), load this
184
+ # extension in each child process, do not load it before forking.
185
+ #
179
186
  # Related module: Sequel::Database::AsyncThreadPool::DatasetMethods
180
187
 
181
188
 
@@ -36,6 +36,8 @@ require 'rbconfig'
36
36
  module Sequel
37
37
  module CallerLogging
38
38
  SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
39
+ RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
40
+ INTERNAL = '<internal'
39
41
 
40
42
  # A regexp of caller lines to ignore, in addition to internal Sequel and Ruby code.
41
43
  attr_accessor :caller_logging_ignore
@@ -59,7 +61,8 @@ module Sequel
59
61
  ignore = caller_logging_ignore
60
62
  c = caller.find do |line|
61
63
  !(line.start_with?(SEQUEL_LIB_PATH) ||
62
- line.start_with?(RbConfig::CONFIG["rubylibdir"]) ||
64
+ line.start_with?(RUBY_STDLIB) ||
65
+ line.start_with?(INTERNAL) ||
63
66
  (ignore && line =~ ignore))
64
67
  end
65
68
 
@@ -403,6 +403,11 @@ module Sequel
403
403
  migrator_class(directory).new(db, directory, opts).is_current?
404
404
  end
405
405
 
406
+ # Lock ID to use for advisory locks when running migrations
407
+ # "sequel-migration".codepoints.reduce(:*) % (2**63)
408
+ MIGRATION_ADVISORY_LOCK_ID = 4966325471869609408
409
+ private_constant :MIGRATION_ADVISORY_LOCK_ID
410
+
406
411
  # Migrates the supplied database using the migration files in the specified directory. Options:
407
412
  # :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
408
413
  # It is very risky to use this option, since it can result in
@@ -416,6 +421,8 @@ module Sequel
416
421
  # :table :: The table containing the schema version (default: :schema_info for integer migrations and
417
422
  # :schema_migrations for timestamped migrations).
418
423
  # :target :: The target version to which to migrate. If not given, migrates to the maximum version.
424
+ # :use_advisory_lock :: Use advisory locks in migrations (only use this if Sequel supports advisory
425
+ # locks for the database).
419
426
  #
420
427
  # Examples:
421
428
  # Sequel::Migrator.run(DB, "migrations")
@@ -423,7 +430,11 @@ module Sequel
423
430
  # Sequel::Migrator.run(DB, "app1/migrations", column: :app2_version)
424
431
  # Sequel::Migrator.run(DB, "app2/migrations", column: :app2_version, table: :schema_info2)
425
432
  def self.run(db, directory, opts=OPTS)
426
- migrator_class(directory).new(db, directory, opts).run
433
+ if opts[:use_advisory_lock]
434
+ db.with_advisory_lock(MIGRATION_ADVISORY_LOCK_ID){run(db, directory, opts.merge(:use_advisory_lock=>false))}
435
+ else
436
+ migrator_class(directory).new(db, directory, opts).run
437
+ end
427
438
  end
428
439
 
429
440
  # Choose the Migrator subclass to use. Uses the TimestampMigrator
@@ -0,0 +1,110 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The provenance dataset extension tracks the locations of all
4
+ # dataset clones that resulted in the current dataset, and includes
5
+ # the information as a comment in the dataset's SQL. This makes it
6
+ # possible to see how a query was built, which can aid debugging.
7
+ # Example:
8
+ #
9
+ # DB[:table].
10
+ # select(:a).
11
+ # where{b > 10}.
12
+ # order(:c).
13
+ # limit(10)
14
+ # # SQL:
15
+ # # SELECT a FROM table WHERE (b > 10) ORDER BY c LIMIT 10 --
16
+ # # -- Dataset Provenance
17
+ # # -- Keys:[:from] Source:(eval at bin/sequel:257):2:in `<main>'
18
+ # # -- Keys:[:select] Source:(eval at bin/sequel:257):3:in `<main>'
19
+ # # -- Keys:[:where] Source:(eval at bin/sequel:257):4:in `<main>'
20
+ # # -- Keys:[:order] Source:(eval at bin/sequel:257):5:in `<main>'
21
+ # # -- Keys:[:limit] Source:(eval at bin/sequel:257):6:in `<main>'
22
+ #
23
+ # With the above example, the source is fairly obvious and not helpful,
24
+ # but in real applications, where datasets can be built from multiple
25
+ # files, seeing where each dataset clone was made can be helpful.
26
+ #
27
+ # The Source listed will skip locations in the Ruby standard library
28
+ # as well as Sequel itself. Other locations can be skipped by
29
+ # providing a Database :provenance_caller_ignore Regexp option:
30
+ #
31
+ # DB.opts[:provenance_caller_ignore] = /\/gems\/library_name-/
32
+ #
33
+ # Related module: Sequel::Dataset::Provenance
34
+
35
+ #
36
+ module Sequel
37
+ class Dataset
38
+ module Provenance
39
+ SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
40
+ RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
41
+ INTERNAL = '<internal'
42
+
43
+ if TRUE_FREEZE
44
+ # Include provenance information when cloning datasets.
45
+ def clone(opts = nil || (return self))
46
+ super(provenance_opts(opts))
47
+ end
48
+ else
49
+ # :nocov:
50
+ def clone(opts = OPTS) # :nodoc:
51
+ super(provenance_opts(opts))
52
+ end
53
+ # :nocov:
54
+ end
55
+
56
+ %w'select insert update delete'.each do |type|
57
+ # Include the provenance information as a comment when preparing dataset SQL
58
+ define_method(:"#{type}_sql") do |*a|
59
+ sql = super(*a)
60
+
61
+ if provenance = @opts[:provenance]
62
+ comment = provenance.map do |hash|
63
+ " -- Keys:#{hash[:keys].inspect} Source:#{hash[:source]}".to_s.gsub(/\s+/, ' ')
64
+ end
65
+ comment << ""
66
+ comment.unshift " -- Dataset Provenance"
67
+ comment.unshift " -- "
68
+ comment = comment.join("\n")
69
+
70
+ if sql.frozen?
71
+ sql += comment
72
+ sql.freeze
73
+ elsif @opts[:append_sql] || @opts[:placeholder_literalizer]
74
+ sql << comment
75
+ else
76
+ sql += comment
77
+ end
78
+ end
79
+
80
+ sql
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ # Return a copy of opts with provenance information added.
87
+ def provenance_opts(opts)
88
+ provenance = {source: provenance_source, keys: opts.keys.freeze}.freeze
89
+ opts = opts.dup
90
+ opts[:provenance] = ((@opts[:provenance] || EMPTY_ARRAY).dup << provenance).freeze
91
+ opts
92
+ end
93
+
94
+ # Return the caller line for the provenance change. This skips
95
+ # Sequel itself and the standard library. Additional locations
96
+ # can be skipped using the :provenance_caller_ignore Dataset option.
97
+ def provenance_source
98
+ ignore = db.opts[:provenance_caller_ignore]
99
+ caller.find do |line|
100
+ !(line.start_with?(SEQUEL_LIB_PATH) ||
101
+ line.start_with?(RUBY_STDLIB) ||
102
+ line.start_with?(INTERNAL) ||
103
+ (ignore && line =~ ignore))
104
+ end
105
+ end
106
+ end
107
+
108
+ register_extension(:provenance, Provenance)
109
+ end
110
+ end
@@ -2,27 +2,34 @@
2
2
  #
3
3
  # The sqlite_json_ops extension adds support to Sequel's DSL to make
4
4
  # it easier to call SQLite JSON functions and operators (added
5
- # first in SQLite 3.38.0).
5
+ # first in SQLite 3.38.0). It also supports the SQLite JSONB functions
6
+ # added in SQLite 3.45.0.
6
7
  #
7
8
  # To load the extension:
8
9
  #
9
10
  # Sequel.extension :sqlite_json_ops
10
11
  #
11
- # This extension works by calling methods on Sequel::SQLite::JSONOp objects,
12
- # which you can create via Sequel.sqlite_json_op:
12
+ # This extension works by calling methods on Sequel::SQLite::JSONOp and
13
+ # Sequel::SQLite::JSONBOp objects, which you can create using
14
+ # Sequel.sqlite_json_op and Sequel.sqlite_jsonb_op:
13
15
  #
14
16
  # j = Sequel.sqlite_json_op(:json_column)
17
+ # jb = Sequel.sqlite_jsonb_op(:jsonb_column)
15
18
  #
16
- # Also, on most Sequel expression objects, you can call the sqlite_json_op method
17
- # to create a Sequel::SQLite::JSONOp object:
19
+ # Also, on most Sequel expression objects, you can call the sqlite_json_op or
20
+ # sqlite_jsonb_op method to create a Sequel::SQLite::JSONOp or
21
+ # Sequel::SQLite::JSONBOp object:
18
22
  #
19
23
  # j = Sequel[:json_column].sqlite_json_op
24
+ # jb = Sequel[:jsonb_column].sqlite_jsonb_op
20
25
  #
21
26
  # If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
22
27
  # or you have loaded the core_refinements extension
23
28
  # and have activated refinements for the file, you can also use Symbol#sqlite_json_op:
29
+ # or Symbol#sqlite_jsonb_op:
24
30
  #
25
31
  # j = :json_column.sqlite_json_op
32
+ # jb = :json_column.sqlite_jsonb_op
26
33
  #
27
34
  # The following methods are available for Sequel::SQLite::JSONOp instances:
28
35
  #
@@ -30,11 +37,13 @@
30
37
  # j.get(1) # (json_column ->> 1)
31
38
  # j.get_text(1) # (json_column -> 1)
32
39
  # j.extract('$.a') # json_extract(json_column, '$.a')
40
+ # jb.extract('$.a') # jsonb_extract(jsonb_column, '$.a')
33
41
  #
34
42
  # j.array_length # json_array_length(json_column)
35
43
  # j.type # json_type(json_column)
36
44
  # j.valid # json_valid(json_column)
37
- # j.json # json(json_column)
45
+ # jb.json # json(jsonb_column)
46
+ # j.jsonb # jsonb(json_column)
38
47
  #
39
48
  # j.insert('$.a', 1) # json_insert(json_column, '$.a', 1)
40
49
  # j.set('$.a', 1) # json_set(json_column, '$.a', 1)
@@ -42,22 +51,30 @@
42
51
  # j.remove('$.a') # json_remove(json_column, '$.a')
43
52
  # j.patch('{"a":2}') # json_patch(json_column, '{"a":2}')
44
53
  #
54
+ # jb.insert('$.a', 1) # jsonb_insert(jsonb_column, '$.a', 1)
55
+ # jb.set('$.a', 1) # jsonb_set(jsonb_column, '$.a', 1)
56
+ # jb.replace('$.a', 1) # jsonb_replace(jsonb_column, '$.a', 1)
57
+ # jb.remove('$.a') # jsonb_remove(jsonb_column, '$.a')
58
+ # jb.patch('{"a":2}') # jsonb_patch(jsonb_column, '{"a":2}')
59
+ #
45
60
  # j.each # json_each(json_column)
46
61
  # j.tree # json_tree(json_column)
47
62
  #
48
- # Related modules: Sequel::SQLite::JSONOp
63
+ # Related modules: Sequel::SQLite::JSONBaseOp, Sequel::SQLite::JSONOp,
64
+ # Sequel::SQLite::JSONBOp
49
65
 
50
66
  #
51
67
  module Sequel
52
68
  module SQLite
53
- # The JSONOp class is a simple container for a single object that
54
- # defines methods that yield Sequel expression objects representing
55
- # SQLite json operators and functions.
69
+ # JSONBaseOp is an abstract base wrapper class for a object that
70
+ # defines methods that return Sequel expression objects representing
71
+ # SQLite json operators and functions. It is subclassed by both
72
+ # JSONOp and JSONBOp for json and jsonb specific behavior.
56
73
  #
57
74
  # In the method documentation examples, assume that:
58
75
  #
59
76
  # json_op = Sequel.sqlite_json_op(:json)
60
- class JSONOp < Sequel::SQL::Wrapper
77
+ class JSONBaseOp < Sequel::SQL::Wrapper
61
78
  GET = ["(".freeze, " ->> ".freeze, ")".freeze].freeze
62
79
  private_constant :GET
63
80
 
@@ -82,7 +99,7 @@ module Sequel
82
99
  # json_op.array_length # json_array_length(json)
83
100
  # json_op.array_length('$[1]') # json_array_length(json, '$[1]')
84
101
  def array_length(*args)
85
- Sequel::SQL::NumericExpression.new(:NOOP, function(:array_length, *args))
102
+ Sequel::SQL::NumericExpression.new(:NOOP, SQL::Function.new(:json_array_length, self, *args))
86
103
  end
87
104
 
88
105
  # Returns an expression for a set of information extracted from the top-level
@@ -92,7 +109,7 @@ module Sequel
92
109
  # json_op.each # json_each(json)
93
110
  # json_op.each('$.a') # json_each(json, '$.a')
94
111
  def each(*args)
95
- function(:each, *args)
112
+ SQL::Function.new(:json_each, self, *args)
96
113
  end
97
114
 
98
115
  # Returns an expression for the JSON array element or object field at the specified
@@ -129,10 +146,17 @@ module Sequel
129
146
  #
130
147
  # json_op.json # json(json)
131
148
  def json
132
- self.class.new(SQL::Function.new(:json, self))
149
+ JSONOp.new(SQL::Function.new(:json, self))
133
150
  end
134
151
  alias minify json
135
152
 
153
+ # Returns the JSONB format of the JSON.
154
+ #
155
+ # json_op.jsonb # jsonb(json)
156
+ def jsonb
157
+ JSONBOp.new(SQL::Function.new(:jsonb, self))
158
+ end
159
+
136
160
  # Returns an expression for updating the JSON object using the RFC 7396 MergePatch algorithm
137
161
  #
138
162
  # json_op.patch('{"a": 1, "b": null}') # json_patch(json, '{"a": 1, "b": null}')
@@ -172,7 +196,7 @@ module Sequel
172
196
  # json_op.tree # json_tree(json)
173
197
  # json_op.tree('$.a') # json_tree(json, '$.a')
174
198
  def tree(*args)
175
- function(:tree, *args)
199
+ SQL::Function.new(:json_tree, self, *args)
176
200
  end
177
201
 
178
202
  # Returns an expression for the type of the JSON value or the JSON value at the given path.
@@ -180,13 +204,13 @@ module Sequel
180
204
  # json_op.type # json_type(json)
181
205
  # json_op.type('$[1]') # json_type(json, '$[1]')
182
206
  def type(*args)
183
- Sequel::SQL::StringExpression.new(:NOOP, function(:type, *args))
207
+ Sequel::SQL::StringExpression.new(:NOOP, SQL::Function.new(:json_type, self, *args))
184
208
  end
185
209
  alias typeof type
186
210
 
187
211
  # Returns a boolean expression for whether the JSON is valid or not.
188
212
  def valid
189
- Sequel::SQL::BooleanExpression.new(:NOOP, function(:valid))
213
+ Sequel::SQL::BooleanExpression.new(:NOOP, SQL::Function.new(:json_valid, self))
190
214
  end
191
215
 
192
216
  private
@@ -198,7 +222,7 @@ module Sequel
198
222
 
199
223
  # Internals of the methods that return functions prefixed with +json_+.
200
224
  def function(name, *args)
201
- SQL::Function.new("json_#{name}", self, *args)
225
+ SQL::Function.new("#{function_prefix}_#{name}", self, *args)
202
226
  end
203
227
 
204
228
  # Internals of the methods that return functions prefixed with +json_+, that
@@ -208,12 +232,36 @@ module Sequel
208
232
  end
209
233
  end
210
234
 
235
+ # JSONOp is used for SQLite json-specific functions and operators.
236
+ class JSONOp < JSONBaseOp
237
+ private
238
+
239
+ def function_prefix
240
+ "json"
241
+ end
242
+ end
243
+
244
+ # JSONOp is used for SQLite jsonb-specific functions and operators.
245
+ class JSONBOp < JSONBaseOp
246
+ private
247
+
248
+ def function_prefix
249
+ "jsonb"
250
+ end
251
+ end
252
+
211
253
  module JSONOpMethods
212
254
  # Wrap the receiver in an JSONOp so you can easily use the SQLite
213
255
  # json functions and operators with it.
214
256
  def sqlite_json_op
215
257
  JSONOp.new(self)
216
258
  end
259
+
260
+ # Wrap the receiver in an JSONBOp so you can easily use the SQLite
261
+ # jsonb functions and operators with it.
262
+ def sqlite_jsonb_op
263
+ JSONBOp.new(self)
264
+ end
217
265
  end
218
266
  end
219
267
 
@@ -227,6 +275,16 @@ module Sequel
227
275
  SQLite::JSONOp.new(v)
228
276
  end
229
277
  end
278
+
279
+ # Return the object wrapped in an SQLite::JSONBOp.
280
+ def sqlite_jsonb_op(v)
281
+ case v
282
+ when SQLite::JSONBOp
283
+ v
284
+ else
285
+ SQLite::JSONBOp.new(v)
286
+ end
287
+ end
230
288
  end
231
289
 
232
290
  class SQL::GenericExpression