sequel 5.57.0 → 5.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +34 -0
- data/README.rdoc +25 -0
- data/bin/sequel +11 -3
- data/doc/cheat_sheet.rdoc +8 -0
- data/doc/opening_databases.rdoc +10 -6
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +5 -0
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
- data/lib/sequel/adapters/jdbc.rb +5 -5
- data/lib/sequel/adapters/mock.rb +1 -1
- data/lib/sequel/adapters/mysql.rb +3 -3
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +58 -17
- data/lib/sequel/adapters/shared/db2.rb +28 -0
- data/lib/sequel/adapters/shared/mssql.rb +35 -1
- data/lib/sequel/adapters/shared/mysql.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +70 -1
- data/lib/sequel/adapters/shared/postgres.rb +94 -18
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +1 -1
- data/lib/sequel/ast_transformer.rb +1 -1
- data/lib/sequel/database/misc.rb +2 -2
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +3 -0
- data/lib/sequel/dataset/actions.rb +49 -0
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/query.rb +62 -0
- data/lib/sequel/dataset/sql.rb +114 -27
- data/lib/sequel/extensions/date_arithmetic.rb +35 -7
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/is_distinct_from.rb +3 -1
- data/lib/sequel/extensions/pg_array.rb +2 -2
- data/lib/sequel/extensions/pg_array_ops.rb +1 -1
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +3 -3
- data/lib/sequel/extensions/pg_inet.rb +2 -2
- data/lib/sequel/extensions/pg_interval.rb +1 -1
- data/lib/sequel/extensions/pg_json.rb +1 -1
- data/lib/sequel/extensions/pg_json_ops.rb +55 -3
- data/lib/sequel/extensions/pg_multirange.rb +2 -2
- data/lib/sequel/extensions/pg_range.rb +2 -2
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +18 -6
- data/lib/sequel/model/base.rb +17 -7
- data/lib/sequel/model/exceptions.rb +1 -1
- data/lib/sequel/model/inflections.rb +6 -6
- data/lib/sequel/plugins/auto_validations.rb +1 -1
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/insert_conflict.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +1 -1
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/nested_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -1
- data/lib/sequel/plugins/sharding.rb +1 -1
- data/lib/sequel/plugins/sql_comments.rb +4 -4
- data/lib/sequel/plugins/subclasses.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -0
- data/lib/sequel/plugins/validation_class_methods.rb +3 -3
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +9 -16
@@ -32,6 +32,10 @@
|
|
32
32
|
#
|
33
33
|
# DB[:table].select(add.as(:d)).where(sub > Sequel::CURRENT_TIMESTAMP)
|
34
34
|
#
|
35
|
+
# On most databases, the values you provide for years/months/days/etc. must
|
36
|
+
# be numeric values and not arbitrary SQL expressions. However, on PostgreSQL
|
37
|
+
# 9.4+, use of arbitrary SQL expressions is supported.
|
38
|
+
#
|
35
39
|
# Related module: Sequel::SQL::DateAdd
|
36
40
|
|
37
41
|
#
|
@@ -54,7 +58,16 @@ module Sequel
|
|
54
58
|
interval = interval.parts
|
55
59
|
end
|
56
60
|
parts = {}
|
57
|
-
interval.each
|
61
|
+
interval.each do |k,v|
|
62
|
+
case v
|
63
|
+
when nil
|
64
|
+
# ignore
|
65
|
+
when Numeric
|
66
|
+
parts[k] = -v
|
67
|
+
else
|
68
|
+
parts[k] = Sequel::SQL::NumericExpression.new(:*, v, -1)
|
69
|
+
end
|
70
|
+
end
|
58
71
|
DateAdd.new(expr, parts, opts)
|
59
72
|
end
|
60
73
|
end
|
@@ -68,6 +81,7 @@ module Sequel
|
|
68
81
|
module DatasetMethods
|
69
82
|
DURATION_UNITS = [:years, :months, :days, :hours, :minutes, :seconds].freeze
|
70
83
|
DEF_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s.freeze}).freeze
|
84
|
+
POSTGRES_DURATION_UNITS = DURATION_UNITS.zip([:years, :months, :days, :hours, :mins, :secs].map{|s| s.to_s.freeze}).freeze
|
71
85
|
MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
|
72
86
|
MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
|
73
87
|
H2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s[0...-1].freeze}).freeze
|
@@ -87,14 +101,28 @@ module Sequel
|
|
87
101
|
|
88
102
|
cast = case db_type = db.database_type
|
89
103
|
when :postgres
|
90
|
-
|
91
|
-
|
92
|
-
|
104
|
+
casted = Sequel.cast(expr, cast_type)
|
105
|
+
|
106
|
+
if db.server_version >= 90400
|
107
|
+
placeholder = []
|
108
|
+
vals = []
|
109
|
+
each_valid_interval_unit(h, POSTGRES_DURATION_UNITS) do |value, sql_unit|
|
110
|
+
placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
|
111
|
+
vals << value
|
112
|
+
end
|
113
|
+
interval = Sequel.function(:make_interval, Sequel.lit(placeholder, *vals)) unless vals.empty?
|
114
|
+
else
|
115
|
+
parts = String.new
|
116
|
+
each_valid_interval_unit(h, DEF_DURATION_UNITS) do |value, sql_unit|
|
117
|
+
parts << "#{value} #{sql_unit} "
|
118
|
+
end
|
119
|
+
interval = Sequel.cast(parts, :interval) unless parts.empty?
|
93
120
|
end
|
94
|
-
|
95
|
-
|
121
|
+
|
122
|
+
if interval
|
123
|
+
return complex_expression_sql_append(sql, :+, [casted, interval])
|
96
124
|
else
|
97
|
-
return
|
125
|
+
return literal_append(sql, casted)
|
98
126
|
end
|
99
127
|
when :sqlite
|
100
128
|
args = [expr]
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# The is_distinct_from extension adds the ability to use the
|
4
4
|
# SQL standard IS DISTINCT FROM operator, which is similar to the
|
5
5
|
# not equals operator, except that NULL values are considered
|
6
|
-
# equal.
|
6
|
+
# equal. PostgreSQL, SQLite 3.39+, and H2 currently support this operator. On
|
7
7
|
# other databases, support is emulated.
|
8
8
|
#
|
9
9
|
# First, you need to load the extension into the database:
|
@@ -90,6 +90,8 @@ module Sequel
|
|
90
90
|
case db.database_type
|
91
91
|
when :postgres, :h2
|
92
92
|
true
|
93
|
+
when :sqlite
|
94
|
+
db.sqlite_version >= 33900
|
93
95
|
else
|
94
96
|
false
|
95
97
|
end
|
@@ -301,7 +301,7 @@ module Sequel
|
|
301
301
|
end
|
302
302
|
end
|
303
303
|
|
304
|
-
unless Sequel::Postgres.
|
304
|
+
unless defined?(Sequel::Postgres.parse_pg_array)
|
305
305
|
require 'strscan'
|
306
306
|
|
307
307
|
# PostgreSQL array parser that handles PostgreSQL array output format.
|
@@ -412,7 +412,7 @@ module Sequel
|
|
412
412
|
@converter = converter
|
413
413
|
end
|
414
414
|
|
415
|
-
if Sequel::Postgres.
|
415
|
+
if defined?(Sequel::Postgres.parse_pg_array)
|
416
416
|
# :nocov:
|
417
417
|
# Use sequel_pg's C-based parser if it has already been defined.
|
418
418
|
def call(string)
|
@@ -144,7 +144,7 @@ module Sequel
|
|
144
144
|
select_hash_groups(Sequel.cast(:enumtypid, Integer).as(:v), :enumlabel).freeze
|
145
145
|
enum_labels.each_value(&:freeze)
|
146
146
|
|
147
|
-
if
|
147
|
+
if defined?(register_array_type)
|
148
148
|
array_types = metadata_dataset.
|
149
149
|
from(:pg_type).
|
150
150
|
where(:oid=>enum_labels.keys).
|
@@ -296,7 +296,7 @@ module Sequel
|
|
296
296
|
|
297
297
|
# Wrap argument in a PGArray if it is an array
|
298
298
|
def wrap_input_array(obj)
|
299
|
-
if obj.is_a?(Array) && Sequel.
|
299
|
+
if obj.is_a?(Array) && defined?(Sequel.pg_array)
|
300
300
|
Sequel.pg_array(obj)
|
301
301
|
else
|
302
302
|
obj
|
@@ -305,7 +305,7 @@ module Sequel
|
|
305
305
|
|
306
306
|
# Wrap argument in an Hstore if it is a hash
|
307
307
|
def wrap_input_hash(obj)
|
308
|
-
if obj.is_a?(Hash) && Sequel.
|
308
|
+
if obj.is_a?(Hash) && defined?(Sequel.hstore)
|
309
309
|
Sequel.hstore(obj)
|
310
310
|
else
|
311
311
|
obj
|
@@ -314,7 +314,7 @@ module Sequel
|
|
314
314
|
|
315
315
|
# Wrap argument in a PGArrayOp if supported
|
316
316
|
def wrap_output_array(obj)
|
317
|
-
if Sequel.
|
317
|
+
if defined?(Sequel.pg_array_op)
|
318
318
|
Sequel.pg_array_op(obj)
|
319
319
|
else
|
320
320
|
obj
|
@@ -49,13 +49,13 @@ module Sequel
|
|
49
49
|
meth = IPAddr.method(:new)
|
50
50
|
add_conversion_proc(869, meth)
|
51
51
|
add_conversion_proc(650, meth)
|
52
|
-
if
|
52
|
+
if defined?(register_array_type)
|
53
53
|
register_array_type('inet', :oid=>1041, :scalar_oid=>869)
|
54
54
|
register_array_type('cidr', :oid=>651, :scalar_oid=>650)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
if
|
58
|
+
if defined?(register_array_type)
|
59
59
|
register_array_type('macaddr', :oid=>1040, :scalar_oid=>829)
|
60
60
|
end
|
61
61
|
@schema_type_classes[:ipaddr] = IPAddr
|
@@ -144,7 +144,7 @@ module Sequel
|
|
144
144
|
db.instance_exec do
|
145
145
|
extend_datasets(IntervalDatasetMethods)
|
146
146
|
add_conversion_proc(1186, Postgres::IntervalDatabaseMethods::PARSER)
|
147
|
-
if
|
147
|
+
if defined?(register_array_type)
|
148
148
|
register_array_type('interval', :oid=>1187, :scalar_oid=>1186)
|
149
149
|
end
|
150
150
|
@schema_type_classes[:interval] = ActiveSupport::Duration
|
@@ -227,7 +227,7 @@ module Sequel
|
|
227
227
|
db.instance_exec do
|
228
228
|
add_conversion_proc(114, method(:_db_parse_json))
|
229
229
|
add_conversion_proc(3802, method(:_db_parse_jsonb))
|
230
|
-
if
|
230
|
+
if defined?(register_array_type)
|
231
231
|
register_array_type('json', :oid=>199, :scalar_oid=>114)
|
232
232
|
register_array_type('jsonb', :oid=>3807, :scalar_oid=>3802)
|
233
233
|
end
|
@@ -123,6 +123,15 @@
|
|
123
123
|
# c = Sequel.pg_jsonb_op(:c)
|
124
124
|
# DB[:t].update(c['key1'] => 1.to_json, c['key2'] => "a".to_json)
|
125
125
|
#
|
126
|
+
# On PostgreSQL 15+, the <tt>IS [NOT] JSON</tt> operator is supported:
|
127
|
+
#
|
128
|
+
# j.is_json # j IS JSON
|
129
|
+
# j.is_json(type: :object) # j IS JSON OBJECT
|
130
|
+
# j.is_json(type: :object, unique: true) # j IS JSON OBJECT WITH UNIQUE
|
131
|
+
# j.is_not_json # j IS NOT JSON
|
132
|
+
# j.is_json(type: :array) # j IS NOT JSON ARRAY
|
133
|
+
# j.is_json(unique: true) # j IS NOT JSON WITH UNIQUE
|
134
|
+
#
|
126
135
|
# If you are also using the pg_json extension, you should load it before
|
127
136
|
# loading this extension. Doing so will allow you to use the #op method on
|
128
137
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
@@ -151,6 +160,18 @@ module Sequel
|
|
151
160
|
GET_PATH = ["(".freeze, " #> ".freeze, ")".freeze].freeze
|
152
161
|
GET_PATH_TEXT = ["(".freeze, " #>> ".freeze, ")".freeze].freeze
|
153
162
|
|
163
|
+
IS_JSON = ["(".freeze, " IS JSON".freeze, "".freeze, ")".freeze].freeze
|
164
|
+
IS_NOT_JSON = ["(".freeze, " IS NOT JSON".freeze, "".freeze, ")".freeze].freeze
|
165
|
+
EMPTY_STRING = Sequel::LiteralString.new('').freeze
|
166
|
+
WITH_UNIQUE = Sequel::LiteralString.new(' WITH UNIQUE').freeze
|
167
|
+
IS_JSON_MAP = {
|
168
|
+
nil => EMPTY_STRING,
|
169
|
+
:value => Sequel::LiteralString.new(' VALUE').freeze,
|
170
|
+
:scalar => Sequel::LiteralString.new(' SCALAR').freeze,
|
171
|
+
:object => Sequel::LiteralString.new(' OBJECT').freeze,
|
172
|
+
:array => Sequel::LiteralString.new(' ARRAY').freeze
|
173
|
+
}.freeze
|
174
|
+
|
154
175
|
# Get JSON array element or object field as json. If an array is given,
|
155
176
|
# gets the object at the specified path.
|
156
177
|
#
|
@@ -233,6 +254,30 @@ module Sequel
|
|
233
254
|
end
|
234
255
|
end
|
235
256
|
|
257
|
+
# Return whether the json object can be parsed as JSON.
|
258
|
+
#
|
259
|
+
# Options:
|
260
|
+
# :type :: Check whether the json object can be parsed as a specific type
|
261
|
+
# of JSON (:value, :scalar, :object, :array).
|
262
|
+
# :unique :: Check JSON objects for unique keys.
|
263
|
+
#
|
264
|
+
# json_op.is_json # json IS JSON
|
265
|
+
# json_op.is_json(type: :object) # json IS JSON OBJECT
|
266
|
+
# json_op.is_json(unique: true) # json IS JSON WITH UNIQUE
|
267
|
+
def is_json(opts=OPTS)
|
268
|
+
_is_json(IS_JSON, opts)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Return whether the json object cannot be parsed as JSON. The opposite
|
272
|
+
# of #is_json. See #is_json for options.
|
273
|
+
#
|
274
|
+
# json_op.is_not_json # json IS NOT JSON
|
275
|
+
# json_op.is_not_json(type: :object) # json IS NOT JSON OBJECT
|
276
|
+
# json_op.is_not_json(unique: true) # json IS NOT JSON WITH UNIQUE
|
277
|
+
def is_not_json(opts=OPTS)
|
278
|
+
_is_json(IS_NOT_JSON, opts)
|
279
|
+
end
|
280
|
+
|
236
281
|
# Returns a set of keys AS text in the json object.
|
237
282
|
#
|
238
283
|
# json_op.keys # json_object_keys(json)
|
@@ -286,6 +331,13 @@ module Sequel
|
|
286
331
|
|
287
332
|
private
|
288
333
|
|
334
|
+
# Internals of IS [NOT] JSON support
|
335
|
+
def _is_json(lit_array, opts)
|
336
|
+
raise Error, "invalid is_json :type option: #{opts[:type].inspect}" unless type = IS_JSON_MAP[opts[:type]]
|
337
|
+
unique = opts[:unique] ? WITH_UNIQUE : EMPTY_STRING
|
338
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(lit_array, [self, type, unique]))
|
339
|
+
end
|
340
|
+
|
289
341
|
# Return a placeholder literal with the given str and args, wrapped
|
290
342
|
# in an JSONOp or JSONBOp, used by operators that return json or jsonb.
|
291
343
|
def json_op(str, args)
|
@@ -306,7 +358,7 @@ module Sequel
|
|
306
358
|
# Automatically wrap argument in a PGArray if it is a plain Array.
|
307
359
|
# Requires that the pg_array extension has been loaded to work.
|
308
360
|
def wrap_array(arg)
|
309
|
-
if arg.instance_of?(Array) && Sequel.
|
361
|
+
if arg.instance_of?(Array) && defined?(Sequel.pg_array)
|
310
362
|
Sequel.pg_array(arg)
|
311
363
|
else
|
312
364
|
arg
|
@@ -600,7 +652,7 @@ module Sequel
|
|
600
652
|
|
601
653
|
# Wrap argument in a PGArray if it is an array
|
602
654
|
def wrap_input_array(obj)
|
603
|
-
if obj.is_a?(Array) && Sequel.
|
655
|
+
if obj.is_a?(Array) && defined?(Sequel.pg_array)
|
604
656
|
Sequel.pg_array(obj)
|
605
657
|
else
|
606
658
|
obj
|
@@ -609,7 +661,7 @@ module Sequel
|
|
609
661
|
|
610
662
|
# Wrap argument in a JSONBArray or JSONBHash if it is an array or hash.
|
611
663
|
def wrap_input_jsonb(obj)
|
612
|
-
if Sequel.
|
664
|
+
if defined?(Sequel.pg_jsonb) && (obj.is_a?(Array) || obj.is_a?(Hash))
|
613
665
|
Sequel.pg_jsonb(obj)
|
614
666
|
else
|
615
667
|
obj
|
@@ -124,7 +124,7 @@ module Sequel
|
|
124
124
|
register_multirange_type('datemultirange', :range_oid=>3912, :oid=>4535)
|
125
125
|
register_multirange_type('int8multirange', :range_oid=>3926, :oid=>4536)
|
126
126
|
|
127
|
-
if
|
127
|
+
if defined?(register_array_type)
|
128
128
|
register_array_type('int4multirange', :oid=>6150, :scalar_oid=>4451, :scalar_typecast=>:int4multirange)
|
129
129
|
register_array_type('nummultirange', :oid=>6151, :scalar_oid=>4532, :scalar_typecast=>:nummultirange)
|
130
130
|
register_array_type('tsmultirange', :oid=>6152, :scalar_oid=>4533, :scalar_typecast=>:tsmultirange)
|
@@ -141,7 +141,7 @@ module Sequel
|
|
141
141
|
add_conversion_proc(4533, PGMultiRange::Creator.new("tsmultirange", procs[3908]))
|
142
142
|
add_conversion_proc(4534, PGMultiRange::Creator.new("tstzmultirange", procs[3910]))
|
143
143
|
|
144
|
-
if
|
144
|
+
if defined?(register_array_type) && defined?(PGArray::Creator)
|
145
145
|
add_conversion_proc(6152, PGArray::Creator.new("tsmultirange", procs[4533]))
|
146
146
|
add_conversion_proc(6153, PGArray::Creator.new("tstzmultirange", procs[4534]))
|
147
147
|
end
|
@@ -139,7 +139,7 @@ module Sequel
|
|
139
139
|
register_range_type('tstzrange', :oid=>3910, :subtype_oid=>1184)
|
140
140
|
register_range_type('daterange', :oid=>3912, :subtype_oid=>1082)
|
141
141
|
register_range_type('int8range', :oid=>3926, :subtype_oid=>20)
|
142
|
-
if
|
142
|
+
if defined?(register_array_type)
|
143
143
|
register_array_type('int4range', :oid=>3905, :scalar_oid=>3904, :scalar_typecast=>:int4range)
|
144
144
|
register_array_type('numrange', :oid=>3907, :scalar_oid=>3906, :scalar_typecast=>:numrange)
|
145
145
|
register_array_type('tsrange', :oid=>3909, :scalar_oid=>3908, :scalar_typecast=>:tsrange)
|
@@ -154,7 +154,7 @@ module Sequel
|
|
154
154
|
procs = conversion_procs
|
155
155
|
add_conversion_proc(3908, Parser.new("tsrange", procs[1114]))
|
156
156
|
add_conversion_proc(3910, Parser.new("tstzrange", procs[1184]))
|
157
|
-
if
|
157
|
+
if defined?(register_array_type) && defined?(PGArray::Creator)
|
158
158
|
add_conversion_proc(3909, PGArray::Creator.new("tsrange", procs[3908]))
|
159
159
|
add_conversion_proc(3911, PGArray::Creator.new("tstzrange", procs[3910]))
|
160
160
|
end
|
@@ -375,7 +375,7 @@ module Sequel
|
|
375
375
|
@row_schema_types = {}
|
376
376
|
extend(@row_type_method_module = Module.new)
|
377
377
|
add_conversion_proc(2249, PGRow::Parser.new(:converter=>PGRow::ArrayRow))
|
378
|
-
if
|
378
|
+
if defined?(register_array_type)
|
379
379
|
register_array_type('record', :oid=>2287, :scalar_oid=>2249)
|
380
380
|
end
|
381
381
|
end
|
@@ -464,7 +464,7 @@ module Sequel
|
|
464
464
|
parser = Parser.new(parser_opts)
|
465
465
|
add_conversion_proc(parser.oid, parser)
|
466
466
|
|
467
|
-
if
|
467
|
+
if defined?(register_array_type) && array_oid && array_oid > 0
|
468
468
|
array_type_name = if type_schema
|
469
469
|
"#{type_schema}.#{type_name}"
|
470
470
|
else
|
@@ -115,13 +115,13 @@ SQL
|
|
115
115
|
# :before_thread_exit :: An object that responds to +call+ that is called before the
|
116
116
|
# the created thread exits.
|
117
117
|
def listen_for_static_cache_updates(models, opts=OPTS)
|
118
|
-
raise Error, "this database object does not respond to listen, use the postgres adapter with the pg driver" unless
|
118
|
+
raise Error, "this database object does not respond to listen, use the postgres adapter with the pg driver" unless defined?(listen)
|
119
119
|
models = [models] unless models.is_a?(Array)
|
120
120
|
raise Error, "array of models to listen for changes cannot be empty" if models.empty?
|
121
121
|
|
122
122
|
oid_map = {}
|
123
123
|
models.each do |model|
|
124
|
-
raise Error, "#{model.inspect} does not use the static_cache plugin" unless model.
|
124
|
+
raise Error, "#{model.inspect} does not use the static_cache plugin" unless defined?(model.load_cache)
|
125
125
|
oid_map[get(regclass_oid(model.dataset.first_source_table))] = model
|
126
126
|
end
|
127
127
|
|
@@ -1717,6 +1717,8 @@ module Sequel
|
|
1717
1717
|
# :graph_select :: A column or array of columns to select from the associated table
|
1718
1718
|
# when eagerly loading the association via +eager_graph+. Defaults to all
|
1719
1719
|
# columns in the associated table.
|
1720
|
+
# :instance_specific :: Marks the association as instance specific. Should be used if the association block
|
1721
|
+
# uses instance specific state, or transient state (accessing current date/time, etc.).
|
1720
1722
|
# :limit :: Limit the number of records to the provided value. Use
|
1721
1723
|
# an array with two elements for the value to specify a
|
1722
1724
|
# limit (first element) and an offset (second element).
|
@@ -1856,6 +1858,16 @@ module Sequel
|
|
1856
1858
|
# in certain places to disable optimizations.
|
1857
1859
|
opts[:instance_specific] = _association_instance_specific_default(name)
|
1858
1860
|
end
|
1861
|
+
if (orig_opts[:instance_specific] || orig_opts[:dataset]) && !opts.has_key?(:allow_eager) && !opts[:eager_loader]
|
1862
|
+
# For associations explicitly marked as instance specific, or that use the
|
1863
|
+
# :dataset option, where :allow_eager is not set, and no :eager_loader is
|
1864
|
+
# provided, disallow eager loading. In these cases, eager loading is
|
1865
|
+
# unlikely to work. This is not done for implicit setting of :instance_specific,
|
1866
|
+
# because implicit use is done by default for all associations with blocks,
|
1867
|
+
# and the vast majority of associations with blocks use the block for filtering
|
1868
|
+
# in a manner compatible with eager loading.
|
1869
|
+
opts[:allow_eager] = false
|
1870
|
+
end
|
1859
1871
|
opts = assoc_class.new.merge!(opts)
|
1860
1872
|
|
1861
1873
|
if opts[:clone] && !opts.cloneable?(cloned_assoc)
|
@@ -3004,7 +3016,7 @@ module Sequel
|
|
3004
3016
|
def complex_expression_sql_append(sql, op, args)
|
3005
3017
|
r = args[1]
|
3006
3018
|
if (((op == :'=' || op == :'!=') && r.is_a?(Sequel::Model)) ||
|
3007
|
-
(multiple = ((op == :IN || op == :'NOT IN') && ((is_ds = r.is_a?(Sequel::Dataset)) || (r.
|
3019
|
+
(multiple = ((op == :IN || op == :'NOT IN') && ((is_ds = r.is_a?(Sequel::Dataset)) || (defined?(r.all?) && r.all?{|x| x.is_a?(Sequel::Model)})))))
|
3008
3020
|
l = args[0]
|
3009
3021
|
if ar = model.association_reflections[l]
|
3010
3022
|
raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
|
@@ -3012,7 +3024,7 @@ module Sequel
|
|
3012
3024
|
if multiple
|
3013
3025
|
klass = ar.associated_class
|
3014
3026
|
if is_ds
|
3015
|
-
if r.
|
3027
|
+
if defined?(r.model)
|
3016
3028
|
unless r.model <= klass
|
3017
3029
|
# A dataset for a different model class, could be a valid regular query
|
3018
3030
|
return super
|
@@ -3344,10 +3356,10 @@ module Sequel
|
|
3344
3356
|
assoc_table_alias = ds.unused_table_alias(alias_base)
|
3345
3357
|
loader = r[:eager_grapher]
|
3346
3358
|
if !associations.empty?
|
3347
|
-
if associations.first.
|
3359
|
+
if defined?(associations.first.call)
|
3348
3360
|
callback = associations.first
|
3349
3361
|
associations = {}
|
3350
|
-
elsif associations.length == 1 && (assocs = associations.first).is_a?(Hash) && assocs.length == 1 && (pr_assoc = assocs.to_a.first) && pr_assoc.first.
|
3362
|
+
elsif associations.length == 1 && (assocs = associations.first).is_a?(Hash) && assocs.length == 1 && (pr_assoc = assocs.to_a.first) && defined?(pr_assoc.first.call)
|
3351
3363
|
callback, assoc = pr_assoc
|
3352
3364
|
associations = assoc.is_a?(Array) ? assoc : [assoc]
|
3353
3365
|
end
|
@@ -3589,10 +3601,10 @@ module Sequel
|
|
3589
3601
|
end
|
3590
3602
|
|
3591
3603
|
associations = eager_assoc[r[:name]]
|
3592
|
-
if associations.
|
3604
|
+
if defined?(associations.call)
|
3593
3605
|
eager_block = associations
|
3594
3606
|
associations = OPTS
|
3595
|
-
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.
|
3607
|
+
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && defined?(pr_assoc.first.call)
|
3596
3608
|
eager_block, associations = pr_assoc
|
3597
3609
|
end
|
3598
3610
|
|
data/lib/sequel/model/base.rb
CHANGED
@@ -492,13 +492,13 @@ module Sequel
|
|
492
492
|
def plugin(plugin, *args, &block)
|
493
493
|
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
494
494
|
|
495
|
-
if !m.
|
495
|
+
if !defined?(m.apply) && !defined?(m.configure) && (!args.empty? || block)
|
496
496
|
Deprecation.deprecate("Plugin #{plugin} accepts no arguments or block, and passing arguments/block to it", "Remove arguments and block when loading the plugin")
|
497
497
|
end
|
498
498
|
|
499
499
|
unless @plugins.include?(m)
|
500
500
|
@plugins << m
|
501
|
-
m.apply(self, *args, &block) if m.
|
501
|
+
m.apply(self, *args, &block) if defined?(m.apply)
|
502
502
|
extend(m::ClassMethods) if m.const_defined?(:ClassMethods, false)
|
503
503
|
include(m::InstanceMethods) if m.const_defined?(:InstanceMethods, false)
|
504
504
|
if m.const_defined?(:DatasetMethods, false)
|
@@ -506,7 +506,7 @@ module Sequel
|
|
506
506
|
end
|
507
507
|
end
|
508
508
|
|
509
|
-
m.configure(self, *args, &block) if m.
|
509
|
+
m.configure(self, *args, &block) if defined?(m.configure)
|
510
510
|
end
|
511
511
|
# :nocov:
|
512
512
|
ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
|
@@ -680,10 +680,11 @@ module Sequel
|
|
680
680
|
|
681
681
|
private
|
682
682
|
|
683
|
-
# Yield to the passed block and if do_raise is false, swallow
|
683
|
+
# Yield to the passed block and if do_raise is false, swallow Sequel::Errors other than DatabaseConnectionError
|
684
|
+
# and DatabaseDisconnectError.
|
684
685
|
def check_non_connection_error(do_raise=require_valid_table)
|
685
686
|
db.transaction(:savepoint=>:only){yield}
|
686
|
-
rescue Sequel::DatabaseConnectionError
|
687
|
+
rescue Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError
|
687
688
|
raise
|
688
689
|
rescue Sequel::Error
|
689
690
|
raise if do_raise
|
@@ -693,9 +694,12 @@ module Sequel
|
|
693
694
|
# this model's dataset.
|
694
695
|
def convert_input_dataset(ds)
|
695
696
|
case ds
|
696
|
-
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
|
697
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
|
697
698
|
self.simple_table = db.literal(ds).freeze
|
698
699
|
ds = db.from(ds)
|
700
|
+
when SQL::AliasedExpression, LiteralString
|
701
|
+
self.simple_table = nil
|
702
|
+
ds = db.from(ds)
|
699
703
|
when Dataset
|
700
704
|
ds = ds.from_self(:alias=>ds.first_source) if ds.joined_dataset?
|
701
705
|
|
@@ -784,7 +788,7 @@ module Sequel
|
|
784
788
|
schema_hash = {}
|
785
789
|
ds_opts = dataset.opts
|
786
790
|
get_columns = proc{check_non_connection_error{columns} || []}
|
787
|
-
schema_array =
|
791
|
+
schema_array = get_db_schema_array(reload) if db.supports_schema_parsing?
|
788
792
|
if schema_array
|
789
793
|
schema_array.each{|k,v| schema_hash[k] = v}
|
790
794
|
|
@@ -821,6 +825,12 @@ module Sequel
|
|
821
825
|
schema_hash
|
822
826
|
end
|
823
827
|
|
828
|
+
# Get the array of schema information for the dataset. Returns nil if
|
829
|
+
# the schema information cannot be determined.
|
830
|
+
def get_db_schema_array(reload)
|
831
|
+
check_non_connection_error(false){db.schema(dataset, :reload=>reload)}
|
832
|
+
end
|
833
|
+
|
824
834
|
# Uncached version of setter_methods, to be overridden by plugins
|
825
835
|
# that want to modify the methods used.
|
826
836
|
def get_setter_methods
|
@@ -99,7 +99,7 @@ module Sequel
|
|
99
99
|
# Convert the given string to CamelCase. Will also convert '/' to '::' which is useful for converting paths to namespaces.
|
100
100
|
def camelize(s)
|
101
101
|
s = s.to_s
|
102
|
-
return s.camelize if s.
|
102
|
+
return s.camelize if defined?(s.camelize)
|
103
103
|
s = s.gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
104
104
|
s
|
105
105
|
end
|
@@ -109,7 +109,7 @@ module Sequel
|
|
109
109
|
# or is not initialized.
|
110
110
|
def constantize(s)
|
111
111
|
s = s.to_s
|
112
|
-
return s.constantize if s.
|
112
|
+
return s.constantize if defined?(s.constantize)
|
113
113
|
raise(NameError, "#{s.inspect} is not a valid constant name!") unless m = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.match(s)
|
114
114
|
Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
|
115
115
|
end
|
@@ -117,14 +117,14 @@ module Sequel
|
|
117
117
|
# Removes the module part from the expression in the string
|
118
118
|
def demodulize(s)
|
119
119
|
s = s.to_s
|
120
|
-
return s.demodulize if s.
|
120
|
+
return s.demodulize if defined?(s.demodulize)
|
121
121
|
s.gsub(/^.*::/, '')
|
122
122
|
end
|
123
123
|
|
124
124
|
# Returns the plural form of the word in the string.
|
125
125
|
def pluralize(s)
|
126
126
|
s = s.to_s
|
127
|
-
return s.pluralize if s.
|
127
|
+
return s.pluralize if defined?(s.pluralize)
|
128
128
|
result = s.dup
|
129
129
|
Inflections.plurals.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(s.downcase)
|
130
130
|
result
|
@@ -133,7 +133,7 @@ module Sequel
|
|
133
133
|
# The reverse of pluralize, returns the singular form of a word in a string.
|
134
134
|
def singularize(s)
|
135
135
|
s = s.to_s
|
136
|
-
return s.singularize if s.
|
136
|
+
return s.singularize if defined?(s.singularize)
|
137
137
|
result = s.dup
|
138
138
|
Inflections.singulars.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(s.downcase)
|
139
139
|
result
|
@@ -143,7 +143,7 @@ module Sequel
|
|
143
143
|
# Also changes '::' to '/' to convert namespaces to paths.
|
144
144
|
def underscore(s)
|
145
145
|
s = s.to_s
|
146
|
-
return s.underscore if s.
|
146
|
+
return s.underscore if defined?(s.underscore)
|
147
147
|
s.gsub('::', '/').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
148
148
|
gsub(/([a-z\d])([A-Z])/, '\1_\2').tr('-', '_').downcase
|
149
149
|
end
|
@@ -252,7 +252,7 @@ module Sequel
|
|
252
252
|
|
253
253
|
unless skip.include?(:unique)
|
254
254
|
unique_opts = Hash[opts[:unique]]
|
255
|
-
if model.
|
255
|
+
if defined?(model.sti_dataset)
|
256
256
|
unique_opts[:dataset] = model.sti_dataset
|
257
257
|
end
|
258
258
|
model.auto_validate_unique_columns.each{|cols| validates_unique(cols, unique_opts)}
|
data/lib/sequel/plugins/dirty.rb
CHANGED
@@ -203,7 +203,7 @@ module Sequel
|
|
203
203
|
get_column_value(column)
|
204
204
|
end
|
205
205
|
|
206
|
-
initial_values[column] = if value && value != true && value.
|
206
|
+
initial_values[column] = if value && value != true && defined?(value.clone)
|
207
207
|
begin
|
208
208
|
value.clone
|
209
209
|
rescue TypeError
|
@@ -36,7 +36,7 @@ module Sequel
|
|
36
36
|
module InsertConflict
|
37
37
|
def self.configure(model)
|
38
38
|
model.instance_exec do
|
39
|
-
if @dataset &&
|
39
|
+
if @dataset && !defined?(@dataset.insert_conflict)
|
40
40
|
raise Error, "#{self}'s dataset does not support insert_conflict"
|
41
41
|
end
|
42
42
|
end
|