sequel 5.82.0 → 5.84.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/sequel +9 -17
- data/lib/sequel/adapters/jdbc/derby.rb +1 -1
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -2
- data/lib/sequel/adapters/shared/postgres.rb +42 -4
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/database/connecting.rb +1 -4
- data/lib/sequel/database/misc.rb +27 -7
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/sql.rb +13 -0
- data/lib/sequel/extensions/pg_json_ops.rb +328 -1
- data/lib/sequel/extensions/stdio_logger.rb +48 -0
- data/lib/sequel/extensions/string_agg.rb +15 -2
- data/lib/sequel/plugins/defaults_setter.rb +16 -4
- data/lib/sequel/plugins/optimistic_locking.rb +2 -0
- data/lib/sequel/sql.rb +8 -5
- data/lib/sequel/version.rb +1 -1
- metadata +4 -235
- data/CHANGELOG +0 -1377
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -104
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -439
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.81.0.txt +0 -31
- data/doc/release_notes/5.82.0.txt +0 -61
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -204
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- data/doc/virtual_rows.rdoc +0 -265
@@ -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
|
-
#
|
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.
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The stdio_logger extension exposes a Sequel::StdioLogger class that
|
4
|
+
# can be used for logging with Sequel, as a minimal alternative to
|
5
|
+
# the logger library. It exposes debug/info/warn/error methods for the
|
6
|
+
# different warning levels. The debug method is a no-op, so that setting
|
7
|
+
# the Database sql_log_level to debug will result in no output for normal
|
8
|
+
# queries. The info/warn/error methods log the current time, log level,
|
9
|
+
# and the given message.
|
10
|
+
#
|
11
|
+
# To use this extension:
|
12
|
+
#
|
13
|
+
# Sequel.extension :stdio_logger
|
14
|
+
#
|
15
|
+
# Then you you can use Sequel::StdioLogger to wrap IO objects that you
|
16
|
+
# would like Sequel to log to:
|
17
|
+
#
|
18
|
+
# DB.loggers << Sequel::StdioLogger.new($stdout)
|
19
|
+
#
|
20
|
+
# log_file = File.open("db_queries.log", 'a')
|
21
|
+
# log_file.sync = true
|
22
|
+
# DB.loggers << Sequel::StdioLogger.new(log_file)
|
23
|
+
#
|
24
|
+
# This is implemented as a global extension instead of a Database extension
|
25
|
+
# because Database loggers must be set before Database extensions are loaded.
|
26
|
+
#
|
27
|
+
# Related module: Sequel::StdioLogger
|
28
|
+
|
29
|
+
#
|
30
|
+
module Sequel
|
31
|
+
class StdioLogger
|
32
|
+
def initialize(device)
|
33
|
+
@device = device
|
34
|
+
end
|
35
|
+
|
36
|
+
# Do not log debug messages. This is so setting the Database
|
37
|
+
# sql_log_level to debug will result in no output.
|
38
|
+
def debug(msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
[:info, :warn, :error].each do |meth|
|
42
|
+
define_method(meth) do |msg|
|
43
|
+
@device.write("#{Time.now.strftime('%F %T')} #{meth.to_s.upcase}: #{msg}\n")
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -54,6 +54,7 @@
|
|
54
54
|
# * MySQL
|
55
55
|
# * HSQLDB
|
56
56
|
# * H2
|
57
|
+
# * SQLite 3.44+ (distinct only works when separator is ',')
|
57
58
|
#
|
58
59
|
# Related module: Sequel::SQL::StringAgg
|
59
60
|
|
@@ -94,6 +95,19 @@ module Sequel
|
|
94
95
|
distinct = sa.is_distinct?
|
95
96
|
|
96
97
|
case db_type = db.database_type
|
98
|
+
when :sqlite
|
99
|
+
raise Error, "string_agg(DISTINCT) is not supported with a non-comma separator on #{db.database_type}" if distinct && separator != ","
|
100
|
+
|
101
|
+
args = [expr]
|
102
|
+
args << separator unless distinct
|
103
|
+
f = Function.new(:group_concat, *args)
|
104
|
+
if order
|
105
|
+
f = f.order(*order)
|
106
|
+
end
|
107
|
+
if distinct
|
108
|
+
f = f.distinct
|
109
|
+
end
|
110
|
+
literal_append(sql, f)
|
97
111
|
when :postgres, :sqlanywhere
|
98
112
|
f = Function.new(db_type == :postgres ? :string_agg : :list, expr, separator)
|
99
113
|
if order
|
@@ -151,7 +165,7 @@ module Sequel
|
|
151
165
|
freeze
|
152
166
|
end
|
153
167
|
|
154
|
-
# Whether the current expression uses distinct expressions
|
168
|
+
# Whether the current expression uses distinct expressions
|
155
169
|
def is_distinct?
|
156
170
|
@distinct == true
|
157
171
|
end
|
@@ -178,4 +192,3 @@ module Sequel
|
|
178
192
|
|
179
193
|
Dataset.register_extension(:string_agg, SQL::StringAgg::DatasetMethods)
|
180
194
|
end
|
181
|
-
|
@@ -26,6 +26,12 @@ module Sequel
|
|
26
26
|
# Album.default_values[:a] = lambda{Date.today}
|
27
27
|
# Album.new.a # => Date.today
|
28
28
|
#
|
29
|
+
# If the proc accepts a single argument, it is passed the instance, allowing
|
30
|
+
# default values to depend on instance-specific state:
|
31
|
+
#
|
32
|
+
# Album.default_values[:a] = lambda{|album| album.b + 1}
|
33
|
+
# Album.new(b: 10).a # => 11
|
34
|
+
#
|
29
35
|
# By default, default values returned are not cached:
|
30
36
|
#
|
31
37
|
# Album.new.a.equal?(Album.new.a) # => false
|
@@ -43,13 +49,13 @@ module Sequel
|
|
43
49
|
# album.values # => {}
|
44
50
|
# album.a
|
45
51
|
# album.values # => {:a => Date.today}
|
46
|
-
#
|
52
|
+
#
|
47
53
|
# Usage:
|
48
54
|
#
|
49
55
|
# # Make all model subclass instances set defaults (called before loading subclasses)
|
50
56
|
# Sequel::Model.plugin :defaults_setter
|
51
57
|
#
|
52
|
-
# # Make the Album class set defaults
|
58
|
+
# # Make the Album class set defaults
|
53
59
|
# Album.plugin :defaults_setter
|
54
60
|
module DefaultsSetter
|
55
61
|
# Set the default values based on the model schema. Options:
|
@@ -76,7 +82,7 @@ module Sequel
|
|
76
82
|
def cache_default_values?
|
77
83
|
@cache_default_values
|
78
84
|
end
|
79
|
-
|
85
|
+
|
80
86
|
# Freeze default values when freezing model class
|
81
87
|
def freeze
|
82
88
|
@default_values.freeze
|
@@ -133,7 +139,13 @@ module Sequel
|
|
133
139
|
def [](k)
|
134
140
|
if new? && !values.has_key?(k)
|
135
141
|
v = model.default_values.fetch(k){return}
|
136
|
-
|
142
|
+
if v.respond_to?(:call)
|
143
|
+
v = if v.respond_to?(:arity) && v.arity == 1
|
144
|
+
v.call(self)
|
145
|
+
else
|
146
|
+
v.call
|
147
|
+
end
|
148
|
+
end
|
137
149
|
values[k] = v if model.cache_default_values?
|
138
150
|
v
|
139
151
|
else
|
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
|
-
|
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(
|
1148
|
+
BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
|
1146
1149
|
when :IN, :"NOT IN"
|
1147
|
-
BooleanExpression.new(
|
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(
|
1159
|
+
BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
|
1157
1160
|
end
|
1158
1161
|
else
|
1159
|
-
BooleanExpression.new(
|
1162
|
+
BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
|
1160
1163
|
end
|
1161
1164
|
end
|
1162
1165
|
when StringExpression, NumericExpression
|
data/lib/sequel/version.rb
CHANGED
@@ -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 =
|
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.
|