sequel 5.83.0 → 5.84.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  3. data/lib/sequel/database/connecting.rb +1 -1
  4. data/lib/sequel/database/misc.rb +8 -3
  5. data/lib/sequel/database/schema_methods.rb +2 -0
  6. data/lib/sequel/extensions/pg_json_ops.rb +328 -1
  7. data/lib/sequel/sql.rb +8 -5
  8. data/lib/sequel/version.rb +1 -1
  9. metadata +2 -236
  10. data/CHANGELOG +0 -1393
  11. data/README.rdoc +0 -936
  12. data/doc/advanced_associations.rdoc +0 -884
  13. data/doc/association_basics.rdoc +0 -1859
  14. data/doc/bin_sequel.rdoc +0 -146
  15. data/doc/cheat_sheet.rdoc +0 -255
  16. data/doc/code_order.rdoc +0 -104
  17. data/doc/core_extensions.rdoc +0 -405
  18. data/doc/dataset_basics.rdoc +0 -96
  19. data/doc/dataset_filtering.rdoc +0 -222
  20. data/doc/extensions.rdoc +0 -77
  21. data/doc/fork_safety.rdoc +0 -84
  22. data/doc/mass_assignment.rdoc +0 -98
  23. data/doc/migration.rdoc +0 -660
  24. data/doc/model_dataset_method_design.rdoc +0 -129
  25. data/doc/model_hooks.rdoc +0 -254
  26. data/doc/model_plugins.rdoc +0 -270
  27. data/doc/mssql_stored_procedures.rdoc +0 -43
  28. data/doc/object_model.rdoc +0 -563
  29. data/doc/opening_databases.rdoc +0 -439
  30. data/doc/postgresql.rdoc +0 -611
  31. data/doc/prepared_statements.rdoc +0 -144
  32. data/doc/querying.rdoc +0 -1070
  33. data/doc/reflection.rdoc +0 -120
  34. data/doc/release_notes/5.0.0.txt +0 -159
  35. data/doc/release_notes/5.1.0.txt +0 -31
  36. data/doc/release_notes/5.10.0.txt +0 -84
  37. data/doc/release_notes/5.11.0.txt +0 -83
  38. data/doc/release_notes/5.12.0.txt +0 -141
  39. data/doc/release_notes/5.13.0.txt +0 -27
  40. data/doc/release_notes/5.14.0.txt +0 -63
  41. data/doc/release_notes/5.15.0.txt +0 -39
  42. data/doc/release_notes/5.16.0.txt +0 -110
  43. data/doc/release_notes/5.17.0.txt +0 -31
  44. data/doc/release_notes/5.18.0.txt +0 -69
  45. data/doc/release_notes/5.19.0.txt +0 -28
  46. data/doc/release_notes/5.2.0.txt +0 -33
  47. data/doc/release_notes/5.20.0.txt +0 -89
  48. data/doc/release_notes/5.21.0.txt +0 -87
  49. data/doc/release_notes/5.22.0.txt +0 -48
  50. data/doc/release_notes/5.23.0.txt +0 -56
  51. data/doc/release_notes/5.24.0.txt +0 -56
  52. data/doc/release_notes/5.25.0.txt +0 -32
  53. data/doc/release_notes/5.26.0.txt +0 -35
  54. data/doc/release_notes/5.27.0.txt +0 -21
  55. data/doc/release_notes/5.28.0.txt +0 -16
  56. data/doc/release_notes/5.29.0.txt +0 -22
  57. data/doc/release_notes/5.3.0.txt +0 -121
  58. data/doc/release_notes/5.30.0.txt +0 -20
  59. data/doc/release_notes/5.31.0.txt +0 -148
  60. data/doc/release_notes/5.32.0.txt +0 -46
  61. data/doc/release_notes/5.33.0.txt +0 -24
  62. data/doc/release_notes/5.34.0.txt +0 -40
  63. data/doc/release_notes/5.35.0.txt +0 -56
  64. data/doc/release_notes/5.36.0.txt +0 -60
  65. data/doc/release_notes/5.37.0.txt +0 -30
  66. data/doc/release_notes/5.38.0.txt +0 -28
  67. data/doc/release_notes/5.39.0.txt +0 -19
  68. data/doc/release_notes/5.4.0.txt +0 -80
  69. data/doc/release_notes/5.40.0.txt +0 -40
  70. data/doc/release_notes/5.41.0.txt +0 -25
  71. data/doc/release_notes/5.42.0.txt +0 -136
  72. data/doc/release_notes/5.43.0.txt +0 -98
  73. data/doc/release_notes/5.44.0.txt +0 -32
  74. data/doc/release_notes/5.45.0.txt +0 -34
  75. data/doc/release_notes/5.46.0.txt +0 -87
  76. data/doc/release_notes/5.47.0.txt +0 -59
  77. data/doc/release_notes/5.48.0.txt +0 -14
  78. data/doc/release_notes/5.49.0.txt +0 -59
  79. data/doc/release_notes/5.5.0.txt +0 -61
  80. data/doc/release_notes/5.50.0.txt +0 -78
  81. data/doc/release_notes/5.51.0.txt +0 -47
  82. data/doc/release_notes/5.52.0.txt +0 -87
  83. data/doc/release_notes/5.53.0.txt +0 -23
  84. data/doc/release_notes/5.54.0.txt +0 -27
  85. data/doc/release_notes/5.55.0.txt +0 -21
  86. data/doc/release_notes/5.56.0.txt +0 -51
  87. data/doc/release_notes/5.57.0.txt +0 -23
  88. data/doc/release_notes/5.58.0.txt +0 -31
  89. data/doc/release_notes/5.59.0.txt +0 -73
  90. data/doc/release_notes/5.6.0.txt +0 -31
  91. data/doc/release_notes/5.60.0.txt +0 -22
  92. data/doc/release_notes/5.61.0.txt +0 -43
  93. data/doc/release_notes/5.62.0.txt +0 -132
  94. data/doc/release_notes/5.63.0.txt +0 -33
  95. data/doc/release_notes/5.64.0.txt +0 -50
  96. data/doc/release_notes/5.65.0.txt +0 -21
  97. data/doc/release_notes/5.66.0.txt +0 -24
  98. data/doc/release_notes/5.67.0.txt +0 -32
  99. data/doc/release_notes/5.68.0.txt +0 -61
  100. data/doc/release_notes/5.69.0.txt +0 -26
  101. data/doc/release_notes/5.7.0.txt +0 -108
  102. data/doc/release_notes/5.70.0.txt +0 -35
  103. data/doc/release_notes/5.71.0.txt +0 -21
  104. data/doc/release_notes/5.72.0.txt +0 -33
  105. data/doc/release_notes/5.73.0.txt +0 -66
  106. data/doc/release_notes/5.74.0.txt +0 -45
  107. data/doc/release_notes/5.75.0.txt +0 -35
  108. data/doc/release_notes/5.76.0.txt +0 -86
  109. data/doc/release_notes/5.77.0.txt +0 -63
  110. data/doc/release_notes/5.78.0.txt +0 -67
  111. data/doc/release_notes/5.79.0.txt +0 -28
  112. data/doc/release_notes/5.8.0.txt +0 -170
  113. data/doc/release_notes/5.80.0.txt +0 -40
  114. data/doc/release_notes/5.81.0.txt +0 -31
  115. data/doc/release_notes/5.82.0.txt +0 -61
  116. data/doc/release_notes/5.83.0.txt +0 -56
  117. data/doc/release_notes/5.9.0.txt +0 -99
  118. data/doc/schema_modification.rdoc +0 -679
  119. data/doc/security.rdoc +0 -443
  120. data/doc/sharding.rdoc +0 -286
  121. data/doc/sql.rdoc +0 -648
  122. data/doc/testing.rdoc +0 -204
  123. data/doc/thread_safety.rdoc +0 -15
  124. data/doc/transactions.rdoc +0 -250
  125. data/doc/validations.rdoc +0 -558
  126. data/doc/virtual_rows.rdoc +0 -265
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66956ac28806c0389a6c809b0aa70d2746a7aff47fd5b039d238c08f621cdbff
4
- data.tar.gz: d2b8d3f88b13432c60e6164fda3577725300e18a0dfa03682871405d074d7ba0
3
+ metadata.gz: 5677b07054121f55f100f0629d8102cabdd9db454263c5199a8f401bea891992
4
+ data.tar.gz: ca57e632e3e66c3a4003f988e5240da6a6eed0de9a143ac3985bd0603e0d9073
5
5
  SHA512:
6
- metadata.gz: 55d45f7c3cb7154f275ddf5ea3649d78a38f147abdb49f1286358d8dbec9f607e3bf43c9ebe1dc31276d28820f455fc82dcce7b4c9808b325288380acf90edf4
7
- data.tar.gz: b8301c7880dcb598ca027d225f5be791200ac1f13f4efe5351222d351b218019351a8efa1f485d499a0650cd4213ba0a148c3f10f0a67aa0b3c04f4ee611b074
6
+ metadata.gz: 2ea8e58679c4ae2455588b584b959b3be691c177823cf336b77fa7b5910e716d9ca88e54024bbef44ec58d3fa8d66070acbf94e8c32a4294126ab2e618f4285e
7
+ data.tar.gz: acf4e565dd754b16ea0c07a11fe67cd21c9d5833ac1cf70c384a8da2c1b7c9ffd324358dbab9a8196d5a249365cc11870e6c807d07dbc631d359b413977f14ed
@@ -349,7 +349,7 @@ module Sequel
349
349
  ps
350
350
  end
351
351
 
352
- # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
352
+ # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options, and VIRTUAL tables with :using option.
353
353
  def create_table_sql(name, generator, options)
354
354
  if options[:strict] && options[:without_rowid]
355
355
  "#{super} STRICT, WITHOUT ROWID"
@@ -357,6 +357,8 @@ module Sequel
357
357
  "#{super} STRICT"
358
358
  elsif options[:without_rowid]
359
359
  "#{super} WITHOUT ROWID"
360
+ elsif options[:using]
361
+ "CREATE VIRTUAL TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)} USING #{options[:using]}"
360
362
  else
361
363
  super
362
364
  end
@@ -34,7 +34,7 @@ module Sequel
34
34
  uri = URI.parse(conn_string)
35
35
  scheme = uri.scheme
36
36
  c = adapter_class(scheme)
37
- opts = c.send(:uri_to_options, uri).merge!(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
37
+ opts = c.send(:options_from_uri, uri).merge!(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
38
38
  end
39
39
  when Hash
40
40
  opts = conn_string.merge(opts)
@@ -72,18 +72,23 @@ module Sequel
72
72
  # Converts a uri to an options hash. These options are then passed
73
73
  # to a newly created database object.
74
74
  def self.uri_to_options(uri)
75
- uri_options = {
75
+ {
76
76
  :user => uri.user,
77
77
  :password => uri.password,
78
78
  :port => uri.port,
79
79
  :host => uri.hostname,
80
80
  :database => (m = /\/(.*)/.match(uri.path)) && (m[1])
81
81
  }
82
+ end
83
+ private_class_method :uri_to_options
84
+
85
+ def self.options_from_uri(uri)
86
+ uri_options = uri_to_options(uri)
82
87
  uri.query.split('&').map{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
83
88
  uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
84
89
  uri_options
85
90
  end
86
- private_class_method :uri_to_options
91
+ private_class_method :options_from_uri
87
92
 
88
93
  # The options hash for this database
89
94
  attr_reader :opts
@@ -266,7 +271,7 @@ module Sequel
266
271
  keys = [:host, :database, :user]
267
272
  opts = self.opts
268
273
  if !keys.any?{|k| opts[k]} && opts[:uri]
269
- opts = self.class.send(:uri_to_options, URI.parse(opts[:uri]))
274
+ opts = self.class.send(:options_from_uri, URI.parse(opts[:uri]))
270
275
  end
271
276
 
272
277
  keys.each do |key|
@@ -191,6 +191,8 @@ module Sequel
191
191
  # The +any+ type is treated like a SQLite column in a non-strict table,
192
192
  # allowing any type of data to be stored. This option is supported on
193
193
  # SQLite 3.37.0+.
194
+ # :using :: Create a VIRTUAL table with the given USING clause. The value should be
195
+ # a string, as it is used directly in the SQL query.
194
196
  # :without_rowid :: Create a WITHOUT ROWID table. Every row in SQLite has a special
195
197
  # 'rowid' column, that uniquely identifies that row within the table.
196
198
  # If this option is used, the 'rowid' column is omitted, which can
@@ -132,6 +132,17 @@
132
132
  # j.is_not_json(type: :array) # j IS NOT JSON ARRAY
133
133
  # j.is_not_json(unique: true) # j IS NOT JSON WITH UNIQUE
134
134
  #
135
+ # On PostgreSQL 17+, the additional JSON functions are supported (see method documentation
136
+ # for additional options):
137
+ #
138
+ # j.exists('$.foo') # json_exists(jsonb_column, '$.foo')
139
+ # j.value('$.foo') # json_value(jsonb_column, '$.foo')
140
+ # j.query('$.foo') # json_query(jsonb_column, '$.foo')
141
+ #
142
+ # j.exists('$.foo', passing: {a: 1}) # json_exists(jsonb_column, '$.foo' PASSING 1 AS a)
143
+ # j.value('$.foo', returning: Time) # json_value(jsonb_column, '$.foo' RETURNING timestamp)
144
+ # j.query('$.foo', wrapper: true) # json_query(jsonb_column, '$.foo' WITH WRAPPER)
145
+ #
135
146
  # If you are also using the pg_json extension, you should load it before
136
147
  # loading this extension. Doing so will allow you to use the #op method on
137
148
  # JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
@@ -224,7 +235,25 @@ module Sequel
224
235
  function(:each_text)
225
236
  end
226
237
 
227
- # Returns a json value for the object at the given path.
238
+ # Return whether the given JSON path yields any items in the receiver.
239
+ # Options:
240
+ #
241
+ # :on_error :: How to handle errors when evaluating the JSON path expression.
242
+ # true :: Return true
243
+ # false :: Return false (default behavior)
244
+ # :null :: Return nil
245
+ # :error :: raise a DatabaseError
246
+ # :passing :: Variables to pass to the JSON path expression. Keys are variable
247
+ # names, values are the values of the variable.
248
+ #
249
+ # json_op.exists("$.a") # json_exists(json, '$.a')
250
+ # json_op.exists("$.a", passing: {a: 1}) # json_exists(json, '$.a' PASSING 1 AS a)
251
+ # json_op.exists("$.a", on_error: :error) # json_exists(json, '$.a' ERROR ON ERROR)
252
+ def exists(path, opts=OPTS)
253
+ Sequel::SQL::BooleanExpression.new(:NOOP, JSONExistsOp.new(self, path, opts))
254
+ end
255
+
256
+ # Returns a JSON value for the object at the given path.
228
257
  #
229
258
  # json_op.extract('a') # json_extract_path(json, 'a')
230
259
  # json_op.extract('a', 'b') # json_extract_path(json, 'a', 'b')
@@ -299,6 +328,35 @@ module Sequel
299
328
  SQL::Function.new(function_name(:populate_recordset), arg, self)
300
329
  end
301
330
 
331
+ # Return the result of applying the JSON path expression to the receiver, by default
332
+ # returning results as jsonb. Options:
333
+ #
334
+ # :on_empty :: How to handle case where path expression yields an empty set.
335
+ # Uses same values as :on_error option.
336
+ # :on_error :: How to handle errors when evaluating the JSON path expression:
337
+ # :null :: Return nil (default)
338
+ # :empty_array :: Return an empty array
339
+ # :empty_object :: Return an empty object
340
+ # :error :: raise a DatabaseError
341
+ # any other value :: used as default value
342
+ # :passing :: Variables to pass to the JSON path expression. Keys are variable
343
+ # names, values are the values of the variable.
344
+ # :returning :: The data type to return (jsonb by default)
345
+ # :wrapper :: How to wrap returned values:
346
+ # true, :unconditional :: Always wrap returning values in an array
347
+ # :conditional :: Only wrap multiple return values in an array
348
+ # :omit_quotes :: Do not wrap scalar strings in quotes
349
+ #
350
+ # json_op.query("$.a") # json_query(json, '$.a')
351
+ # json_op.query("$.a", passing: {a: 1}) # json_query(json, '$.a' PASSING 1 AS a)
352
+ # json_op.query("$.a", on_error: :empty_array) # json_query(json, '$.a' EMPTY ARRAY ON ERROR)
353
+ # json_op.query("$.a", returning: Time) # json_query(json, '$.a' RETURNING timestamp)
354
+ # json_op.query("$.a", on_empty: 2) # json_query(json, '$.a' DEFAULT 2 ON EMPTY)
355
+ # json_op.query("$.a", wrapper: true) # json_query(json, '$.a' WITH WRAPPER)
356
+ def query(path, opts=OPTS)
357
+ self.class.new(JSONQueryOp.new(self, path, opts))
358
+ end
359
+
302
360
  # Returns a json value stripped of all internal null values.
303
361
  #
304
362
  # json_op.strip_nulls # json_strip_nulls(json)
@@ -329,6 +387,34 @@ module Sequel
329
387
  function(:typeof)
330
388
  end
331
389
 
390
+ # If called without arguments, operates as SQL::Wrapper#value. Otherwise,
391
+ # return the result of applying the JSON path expression to the receiver, by default
392
+ # returning results as text. Options:
393
+ #
394
+ # :on_empty :: How to handle case where path expression yields an empty set.
395
+ # Uses same values as :on_error option.
396
+ # :on_error :: How to handle errors when evaluating the JSON path expression.
397
+ # :null :: Return nil (default)
398
+ # :error :: raise a DatabaseError
399
+ # any other value :: used as default value
400
+ # :passing :: Variables to pass to the JSON path expression. Keys are variable
401
+ # names, values are the values of the variable.
402
+ # :returning :: The data type to return (text by default)
403
+ #
404
+ # json_op.value("$.a") # json_value(json, '$.a')
405
+ # json_op.value("$.a", passing: {a: 1}) # json_value(json, '$.a' PASSING 1 AS a)
406
+ # json_op.value("$.a", on_error: :error) # json_value(json, '$.a' ERROR ON ERROR)
407
+ # json_op.value("$.a", returning: Time) # json_value(json, '$.a' RETURNING timestamp)
408
+ # json_op.value("$.a", on_empty: 2) # json_value(json, '$.a' DEFAULT 2 ON EMPTY)
409
+ def value(path=(no_args_given = true), opts=OPTS)
410
+ if no_args_given
411
+ # Act as SQL::Wrapper#value
412
+ super()
413
+ else
414
+ Sequel::SQL::StringExpression.new(:NOOP, JSONValueOp.new(self, path, opts))
415
+ end
416
+ end
417
+
332
418
  private
333
419
 
334
420
  # Internals of IS [NOT] JSON support
@@ -705,6 +791,247 @@ module Sequel
705
791
  end
706
792
  end
707
793
 
794
+ # Object representing json_exists calls
795
+ class JSONExistsOp < SQL::Expression
796
+ ON_ERROR_SQL = {
797
+ true => 'TRUE',
798
+ false => 'FALSE',
799
+ :null => 'UNKNOWN',
800
+ :error => 'ERROR',
801
+ }.freeze
802
+ private_constant :ON_ERROR_SQL
803
+
804
+ # Expression (context_item in PostgreSQL terms), usually JSONBaseOp instance
805
+ attr_reader :expr
806
+
807
+ # JSON path expression to apply against the expression
808
+ attr_reader :path
809
+
810
+ # Variables to set in the JSON path expression
811
+ attr_reader :passing
812
+
813
+ # How to handle errors when evaluating the JSON path expression
814
+ attr_reader :on_error
815
+
816
+ # See JSONBaseOp#exists for documentation on the options.
817
+ def initialize(expr, path, opts=OPTS)
818
+ @expr = expr
819
+ @path = path
820
+ @passing = opts[:passing]
821
+ @on_error = opts[:on_error]
822
+ freeze
823
+ end
824
+
825
+ # Append the SQL function call expression to the SQL
826
+ def to_s_append(ds, sql)
827
+ to_s_append_function_name(ds, sql)
828
+ to_s_append_args_passing(ds, sql)
829
+ to_s_append_on_error(ds, sql)
830
+ sql << ')'
831
+ end
832
+
833
+ # Support transforming of function call expression
834
+ def sequel_ast_transform(transformer)
835
+ opts = {}
836
+ transform_opts(transformer, opts)
837
+ self.class.new(transformer.call(@expr), @path, opts)
838
+ end
839
+
840
+ private
841
+
842
+ # Set the :passing and :on_error options when doing an
843
+ # AST transform.
844
+ def transform_opts(transformer, opts)
845
+ if @passing
846
+ passing = opts[:passing] = {}
847
+ @passing.each do |k, v|
848
+ passing[k] = transformer.call(v)
849
+ end
850
+ end
851
+
852
+ opts[:on_error] = @on_error
853
+ end
854
+
855
+ def to_s_append_function_name(ds, sql)
856
+ sql << 'json_exists('
857
+ end
858
+
859
+ # Append the expression, path, and optional PASSING fragments
860
+ def to_s_append_args_passing(ds, sql)
861
+ ds.literal_append(sql, @expr)
862
+ sql << ', '
863
+ ds.literal_append(sql, @path)
864
+
865
+ if (passing = @passing) && !passing.empty?
866
+ sql << ' PASSING '
867
+ comma = false
868
+ passing.each do |k, v|
869
+ if comma
870
+ sql << ', '
871
+ else
872
+ comma = true
873
+ end
874
+ ds.literal_append(sql, v)
875
+ sql << " AS " << k.to_s
876
+ end
877
+ end
878
+ end
879
+
880
+ # Append the optional ON ERROR fragments
881
+ def to_s_append_on_error(ds, sql)
882
+ unless @on_error.nil?
883
+ sql << " "
884
+ to_s_append_on_value(ds, sql, @on_error)
885
+ sql << " ON ERROR"
886
+ end
887
+ end
888
+
889
+ # Append the value to use for ON ERROR
890
+ def to_s_append_on_value(ds, sql, value)
891
+ sql << ON_ERROR_SQL.fetch(value)
892
+ end
893
+ end
894
+
895
+ # Object representing json_value calls
896
+ class JSONValueOp < JSONExistsOp
897
+ ON_SQL = {
898
+ :null => 'NULL',
899
+ :error => 'ERROR',
900
+ }.freeze
901
+ private_constant :ON_SQL
902
+
903
+ # The database type to cast returned values to
904
+ attr_reader :returning
905
+
906
+ # How to handle cases where the JSON path expression evaluation yields
907
+ # an empty set.
908
+ attr_reader :on_empty
909
+
910
+ # See JSONBaseOp#value for documentation of the options.
911
+ def initialize(expr, path, opts=OPTS)
912
+ @returning = opts[:returning]
913
+ @on_empty = opts[:on_empty]
914
+ super
915
+ end
916
+
917
+ private
918
+
919
+ # Also handle transforming the returning and on_empty options.
920
+ def transform_opts(transformer, opts)
921
+ super
922
+ opts[:returning] = @returning
923
+ on_error = @on_error
924
+ on_error = transformer.call(on_error) unless on_sql_value(on_error)
925
+ opts[:on_error] = on_error
926
+ on_empty = @on_empty
927
+ on_empty = transformer.call(on_empty) unless on_sql_value(on_empty)
928
+ opts[:on_empty] = on_empty
929
+ end
930
+
931
+ def to_s_append_function_name(ds, sql)
932
+ sql << 'json_value('
933
+ end
934
+
935
+ # Also append the optional RETURNING fragment
936
+ def to_s_append_args_passing(ds, sql)
937
+ super
938
+
939
+ if @returning
940
+ sql << ' RETURNING ' << ds.db.cast_type_literal(@returning).to_s
941
+ end
942
+ end
943
+
944
+ # Also append the optional ON EMPTY fragment
945
+ def to_s_append_on_error(ds, sql)
946
+ unless @on_empty.nil?
947
+ sql << " "
948
+ to_s_append_on_value(ds, sql, @on_empty)
949
+ sql << " ON EMPTY"
950
+ end
951
+
952
+ super
953
+ end
954
+
955
+ # Handle DEFAULT values in ON EMPTY/ON ERROR fragments
956
+ def to_s_append_on_value(ds, sql, value)
957
+ if v = on_sql_value(value)
958
+ sql << v
959
+ else
960
+ sql << 'DEFAULT '
961
+ default_literal_append(ds, sql, value)
962
+ end
963
+ end
964
+
965
+ # Do not auto paramterize default value, as PostgreSQL doesn't allow it.
966
+ def default_literal_append(ds, sql, v)
967
+ if sql.respond_to?(:skip_auto_param)
968
+ sql.skip_auto_param do
969
+ ds.literal_append(sql, v)
970
+ end
971
+ else
972
+ ds.literal_append(sql, v)
973
+ end
974
+ end
975
+
976
+ def on_sql_value(value)
977
+ ON_SQL[value]
978
+ end
979
+ end
980
+
981
+ # Object representing json_query calls
982
+ class JSONQueryOp < JSONValueOp
983
+ ON_SQL = {
984
+ :null => 'NULL',
985
+ :error => 'ERROR',
986
+ :empty_array => 'EMPTY ARRAY',
987
+ :empty_object => 'EMPTY OBJECT',
988
+ }.freeze
989
+ private_constant :ON_SQL
990
+
991
+ WRAPPER = {
992
+ :conditional => ' WITH CONDITIONAL WRAPPER',
993
+ :unconditional => ' WITH WRAPPER',
994
+ :omit_quotes => ' OMIT QUOTES'
995
+ }
996
+ WRAPPER[true] = WRAPPER[:unconditional]
997
+ WRAPPER.freeze
998
+ private_constant :WRAPPER
999
+
1000
+ # How to handle wrapping of results
1001
+ attr_reader :wrapper
1002
+
1003
+ # See JSONBaseOp#query for documentation of the options.
1004
+ def initialize(expr, path, opts=OPTS)
1005
+ @wrapper = opts[:wrapper]
1006
+ super
1007
+ end
1008
+
1009
+ private
1010
+
1011
+ # Also handle transforming the wrapper option
1012
+ def transform_opts(transformer, opts)
1013
+ super
1014
+ opts[:wrapper] = @wrapper
1015
+ end
1016
+
1017
+ def to_s_append_function_name(ds, sql)
1018
+ sql << 'json_query('
1019
+ end
1020
+
1021
+ # Also append the optional WRAPPER/OMIT QUOTES fragment
1022
+ def to_s_append_args_passing(ds, sql)
1023
+ super
1024
+
1025
+ if @wrapper
1026
+ sql << WRAPPER.fetch(@wrapper)
1027
+ end
1028
+ end
1029
+
1030
+ def on_sql_value(value)
1031
+ ON_SQL[value]
1032
+ end
1033
+ end
1034
+
708
1035
  module JSONOpMethods
709
1036
  # Wrap the receiver in an JSONOp so you can easily use the PostgreSQL
710
1037
  # json functions and operators with it.
data/lib/sequel/sql.rb CHANGED
@@ -153,13 +153,16 @@ module Sequel
153
153
  class ComplexExpression < Expression
154
154
  # A hash of the opposite for each operator symbol, used for inverting
155
155
  # objects.
156
- OPERTATOR_INVERSIONS = {:AND => :OR, :OR => :AND, :< => :>=, :> => :<=,
156
+ OPERATOR_INVERSIONS = {:AND => :OR, :OR => :AND, :< => :>=, :> => :<=,
157
157
  :<= => :>, :>= => :<, :'=' => :'!=' , :'!=' => :'=', :LIKE => :'NOT LIKE',
158
158
  :'NOT LIKE' => :LIKE, :~ => :'!~', :'!~' => :~, :IN => :'NOT IN',
159
159
  :'NOT IN' => :IN, :IS => :'IS NOT', :'IS NOT' => :IS, :'~*' => :'!~*',
160
160
  :'!~*' => :'~*', :NOT => :NOOP, :NOOP => :NOT, :ILIKE => :'NOT ILIKE',
161
161
  :'NOT ILIKE'=>:ILIKE}.freeze
162
162
 
163
+ # SEQUEL6: Remove
164
+ OPERTATOR_INVERSIONS = OPERATOR_INVERSIONS
165
+
163
166
  # Standard mathematical operators used in +NumericMethods+
164
167
  MATHEMATICAL_OPERATORS = [:+, :-, :/, :*, :**].freeze
165
168
 
@@ -1142,9 +1145,9 @@ module Sequel
1142
1145
  when BooleanExpression
1143
1146
  case op = ce.op
1144
1147
  when :AND, :OR
1145
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
1148
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
1146
1149
  when :IN, :"NOT IN"
1147
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1150
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1148
1151
  else
1149
1152
  if ce.args.length == 2
1150
1153
  case ce.args[1]
@@ -1153,10 +1156,10 @@ module Sequel
1153
1156
  # can result in incorrect behavior for ANY/SOME/ALL operators.
1154
1157
  BooleanExpression.new(:NOT, ce)
1155
1158
  else
1156
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1159
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1157
1160
  end
1158
1161
  else
1159
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1162
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1160
1163
  end
1161
1164
  end
1162
1165
  when StringExpression, NumericExpression
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 83
9
+ MINOR = 84
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.