sequel 5.8.0 → 5.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +30 -0
- data/doc/release_notes/5.9.0.txt +99 -0
- data/doc/testing.rdoc +10 -10
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +1 -1
- data/lib/sequel/adapters/jdbc.rb +19 -7
- data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -5
- data/lib/sequel/adapters/postgres.rb +3 -3
- data/lib/sequel/adapters/shared/access.rb +5 -6
- data/lib/sequel/adapters/shared/mysql.rb +28 -2
- data/lib/sequel/adapters/shared/postgres.rb +16 -6
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/connection_pool.rb +2 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +12 -4
- data/lib/sequel/connection_pool/threaded.rb +19 -7
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +6 -6
- data/lib/sequel/database/misc.rb +3 -3
- data/lib/sequel/database/query.rb +2 -2
- data/lib/sequel/database/schema_generator.rb +9 -3
- data/lib/sequel/database/schema_methods.rb +12 -5
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +4 -4
- data/lib/sequel/dataset/query.rb +5 -0
- data/lib/sequel/dataset/sql.rb +8 -6
- data/lib/sequel/extensions/escaped_like.rb +100 -0
- data/lib/sequel/extensions/eval_inspect.rb +3 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -3
- data/lib/sequel/extensions/pg_extended_date_support.rb +23 -10
- data/lib/sequel/model/associations.rb +18 -4
- data/lib/sequel/model/base.rb +9 -2
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +5 -7
- data/lib/sequel/plugins/sharding.rb +2 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
- data/lib/sequel/plugins/tree.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +2 -2
- data/lib/sequel/version.rb +4 -1
- data/spec/adapters/mysql_spec.rb +24 -0
- data/spec/adapters/postgres_spec.rb +9 -9
- data/spec/adapters/sqlite_spec.rb +10 -10
- data/spec/core/connection_pool_spec.rb +22 -0
- data/spec/core/database_spec.rb +6 -6
- data/spec/core/dataset_spec.rb +16 -5
- data/spec/core/expression_filters_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -1
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/connection_expiration_spec.rb +20 -2
- data/spec/extensions/connection_validator_spec.rb +20 -3
- data/spec/extensions/escaped_like_spec.rb +40 -0
- data/spec/extensions/eval_inspect_spec.rb +1 -1
- data/spec/extensions/nested_attributes_spec.rb +6 -0
- data/spec/extensions/pg_array_spec.rb +13 -13
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +0 -1
- data/spec/extensions/pg_range_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_agg_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +2 -2
- data/spec/extensions/validation_helpers_spec.rb +1 -1
- data/spec/integration/associations_test.rb +12 -0
- data/spec/integration/dataset_test.rb +21 -0
- data/spec/integration/type_test.rb +4 -4
- data/spec/model/base_spec.rb +9 -0
- data/spec/model/eager_loading_spec.rb +25 -0
- data/spec/model/record_spec.rb +1 -1
- metadata +6 -2
data/lib/sequel/dataset/query.rb
CHANGED
@@ -113,6 +113,11 @@ module Sequel
|
|
113
113
|
# DB[:items].distinct # SQL: SELECT DISTINCT * FROM items
|
114
114
|
# DB[:items].order(:id).distinct(:id) # SQL: SELECT DISTINCT ON (id) * FROM items ORDER BY id
|
115
115
|
# DB[:items].order(:id).distinct{func(:id)} # SQL: SELECT DISTINCT ON (func(id)) * FROM items ORDER BY id
|
116
|
+
#
|
117
|
+
# There is support for emualting the DISTINCT ON support in MySQL, but it
|
118
|
+
# does not support the ORDER of the dataset, and also doesn't work in many
|
119
|
+
# cases if the ONLY_FULL_GROUP_BY sql_mode is used, which is the default on
|
120
|
+
# MySQL 5.7.5+.
|
116
121
|
def distinct(*args, &block)
|
117
122
|
virtual_row_columns(args, block)
|
118
123
|
if args.empty?
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -28,7 +28,7 @@ module Sequel
|
|
28
28
|
|
29
29
|
case values.size
|
30
30
|
when 0
|
31
|
-
return insert_sql(
|
31
|
+
return insert_sql(OPTS)
|
32
32
|
when 1
|
33
33
|
case vals = values[0]
|
34
34
|
when Hash
|
@@ -389,8 +389,10 @@ module Sequel
|
|
389
389
|
literal_append(sql, args[0])
|
390
390
|
sql << ' ' << op.to_s << ' '
|
391
391
|
literal_append(sql, args[1])
|
392
|
-
|
393
|
-
|
392
|
+
if requires_like_escape?
|
393
|
+
sql << " ESCAPE "
|
394
|
+
literal_append(sql, "\\")
|
395
|
+
end
|
394
396
|
sql << ')'
|
395
397
|
when :ILIKE, :'NOT ILIKE'
|
396
398
|
complex_expression_sql_append(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), args.map{|v| Sequel.function(:UPPER, v)})
|
@@ -577,7 +579,7 @@ module Sequel
|
|
577
579
|
sql << str
|
578
580
|
else
|
579
581
|
re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
|
580
|
-
|
582
|
+
while true
|
581
583
|
previous, q, str = str.partition(re)
|
582
584
|
sql << previous
|
583
585
|
literal_append(sql, args[($1||q[1..-1].to_s).to_sym]) unless q.empty?
|
@@ -596,13 +598,13 @@ module Sequel
|
|
596
598
|
else
|
597
599
|
i = -1
|
598
600
|
match_len = args.length - 1
|
599
|
-
|
601
|
+
while true
|
600
602
|
previous, q, str = str.partition('?')
|
601
603
|
sql << previous
|
602
604
|
literal_append(sql, args.at(i+=1)) unless q.empty?
|
603
605
|
if str.empty?
|
604
606
|
unless i == match_len
|
605
|
-
raise Error, "Mismatched number of placeholders (#{i+1}) and placeholder arguments (#{args.length}) when using placeholder
|
607
|
+
raise Error, "Mismatched number of placeholders (#{i+1}) and placeholder arguments (#{args.length}) when using placeholder string"
|
606
608
|
end
|
607
609
|
break
|
608
610
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The escaped_like extension adds +escaped_like+ and +escaped_ilike+
|
4
|
+
# methods to Sequel::SQL::StringMethods, which allow them to be easily
|
5
|
+
# used with most of Sequel's expression objects. Example:
|
6
|
+
#
|
7
|
+
# DB[:table].where{string_column.escaped_like('?%', user_input)}
|
8
|
+
# # user_input is 'foo':
|
9
|
+
# # SELECT * FROM table WHERE string_column LIKE 'foo%' ESCAPE '\'
|
10
|
+
# # user_input is '%foo':
|
11
|
+
# # SELECT * FROM table WHERE string_column LIKE '\%foo%' ESCAPE '\'
|
12
|
+
#
|
13
|
+
# To load the extension:
|
14
|
+
#
|
15
|
+
# Sequel.extension :escaped_like
|
16
|
+
#
|
17
|
+
# Related modules: Sequel::SQL::StringMethods, Sequel::SQL::EscapedLikeExpression
|
18
|
+
|
19
|
+
#
|
20
|
+
module Sequel
|
21
|
+
module SQL
|
22
|
+
# Represents an pattern match SQL expression, where the pattern can depend
|
23
|
+
# upon interpolated values in a database-dependent manner.
|
24
|
+
class EscapedLikeExpression < Expression
|
25
|
+
include AliasMethods
|
26
|
+
include BooleanMethods
|
27
|
+
include CastMethods
|
28
|
+
include OrderMethods
|
29
|
+
|
30
|
+
# Initialize the expression. Arguments:
|
31
|
+
# expr :: Right hand site of LIKE/ILIKE operator, what you are matching against the pattern
|
32
|
+
# case_insensitive :: Whether the match is case sensitive
|
33
|
+
# placeholder_pattern :: The pattern to match against, with +?+ for the placeholders
|
34
|
+
# placeholder_values :: The string values for each +?+ in the placeholder pattern. Should be an
|
35
|
+
# array of strings, though it can be a single string if there is only
|
36
|
+
# a single placeholder.
|
37
|
+
def initialize(expr, case_sensitive, placeholder_pattern, placeholder_values)
|
38
|
+
@expr = expr
|
39
|
+
@method = case_sensitive ? :like : :ilike
|
40
|
+
@pattern = placeholder_pattern
|
41
|
+
unless placeholder_values.is_a?(Array)
|
42
|
+
placeholder_values = [placeholder_values].freeze
|
43
|
+
end
|
44
|
+
@values = placeholder_values
|
45
|
+
freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
# Interpolate the pattern values into the placeholder pattern to get the final pattern,
|
49
|
+
# now that we have access to the dataset. Use the expression and final pattern and
|
50
|
+
# add an appropriate LIKE/ILIKE expression to the SQL being built.
|
51
|
+
def to_s_append(ds, sql)
|
52
|
+
i = -1
|
53
|
+
match_len = @values.length - 1
|
54
|
+
like_pattern = String.new
|
55
|
+
pattern = @pattern
|
56
|
+
while true
|
57
|
+
previous, q, pattern = pattern.partition('?')
|
58
|
+
like_pattern << previous
|
59
|
+
|
60
|
+
unless q.empty?
|
61
|
+
if i == match_len
|
62
|
+
raise Error, "Mismatched number of placeholders (#{i+1}) and placeholder arguments (#{@values.length}) for escaped like expression: #{@pattern.inspect}"
|
63
|
+
end
|
64
|
+
like_pattern << ds.escape_like(@values.at(i+=1))
|
65
|
+
end
|
66
|
+
|
67
|
+
if pattern.empty?
|
68
|
+
unless i == match_len
|
69
|
+
raise Error, "Mismatched number of placeholders (#{i+1}) and placeholder arguments (#{@values.length}) for escaped like expression: #{@pattern.inspect}"
|
70
|
+
end
|
71
|
+
break
|
72
|
+
end
|
73
|
+
end
|
74
|
+
ds.literal_append(sql, Sequel.send(@method, @expr, like_pattern))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module StringMethods
|
79
|
+
# Create a +EscapedLikeExpression+ case insensitive pattern match of the receiver
|
80
|
+
# with the patterns, interpolated escaped values for each +?+ placeholder in the
|
81
|
+
# pattern.
|
82
|
+
#
|
83
|
+
# Sequel[:a].escaped_ilike('?%', 'A') # "a" ILIKE 'A%' ESCAPE '\'
|
84
|
+
# Sequel[:a].escaped_ilike('?%', '%A') # "a" ILIKE '\%A%' ESCAPE '\'
|
85
|
+
def escaped_ilike(placeholder_pattern, placeholder_values)
|
86
|
+
EscapedLikeExpression.new(self, false, placeholder_pattern, placeholder_values)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Create a +EscapedLikeExpression+ case sensitive pattern match of the receiver
|
90
|
+
# with the patterns, interpolated escaped values for each +?+ placeholder in the
|
91
|
+
# pattern.
|
92
|
+
#
|
93
|
+
# Sequel[:a].escaped_like('?%', 'A') # "a" LIKE 'A%' ESCAPE '\'
|
94
|
+
# Sequel[:a].escaped_like('?%', '%A') # "a" LIKE '\%A%' ESCAPE '\'
|
95
|
+
def escaped_like(placeholder_pattern, placeholder_values)
|
96
|
+
EscapedLikeExpression.new(self, true, placeholder_pattern, placeholder_values)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -26,7 +26,9 @@ module Sequel
|
|
26
26
|
# for eval.
|
27
27
|
def eval_inspect(obj)
|
28
28
|
case obj
|
29
|
-
when
|
29
|
+
when BigDecimal
|
30
|
+
"BigDecimal(#{obj.to_s.inspect})"
|
31
|
+
when Sequel::SQL::Blob, Sequel::LiteralString
|
30
32
|
"#{obj.class}.new(#{obj.to_s.inspect})"
|
31
33
|
when Sequel::SQL::ValueList
|
32
34
|
"#{obj.class}.new(#{obj.to_a.inspect})"
|
@@ -36,14 +36,14 @@ module Sequel
|
|
36
36
|
|
37
37
|
if RUBY_VERSION >= '2.4'
|
38
38
|
def _typecast_value_string_to_decimal(value)
|
39
|
-
BigDecimal
|
39
|
+
BigDecimal(value)
|
40
40
|
rescue
|
41
|
-
BigDecimal
|
41
|
+
BigDecimal('0.0')
|
42
42
|
end
|
43
43
|
else
|
44
44
|
# :nocov:
|
45
45
|
def _typecast_value_string_to_decimal(value)
|
46
|
-
BigDecimal
|
46
|
+
BigDecimal(value)
|
47
47
|
end
|
48
48
|
# :nocov:
|
49
49
|
end
|
@@ -86,9 +86,19 @@ module Sequel
|
|
86
86
|
if value.is_a?(String) && (m = value.match(/(?:(?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/)) && (m[1] || m[2])
|
87
87
|
if m[2]
|
88
88
|
value = value.sub(' BC', '').sub(' ', ' BC ')
|
89
|
+
conv = defined?(JRUBY_VERSION) && JRUBY_VERSION == '9.2.0.0'
|
89
90
|
end
|
90
|
-
if m[1]
|
91
|
+
if m[1] || conv
|
91
92
|
dt = DateTime.parse(value)
|
93
|
+
if conv
|
94
|
+
# :nocov:
|
95
|
+
if Sequel.datetime_class == DateTime
|
96
|
+
dt >>= 12
|
97
|
+
else
|
98
|
+
dt >>= 24
|
99
|
+
end
|
100
|
+
# :nocov:
|
101
|
+
end
|
92
102
|
dt = dt.to_time unless Sequel.datetime_class == DateTime
|
93
103
|
Sequel.convert_output_timestamp(dt, Sequel.application_timezone)
|
94
104
|
else
|
@@ -191,15 +201,6 @@ module Sequel
|
|
191
201
|
if RUBY_ENGINE == 'jruby'
|
192
202
|
# :nocov:
|
193
203
|
|
194
|
-
# Work around JRuby bug #4822 in Time#to_datetime for times before date of calendar reform
|
195
|
-
def literal_time(time)
|
196
|
-
if time < TIME_YEAR_1
|
197
|
-
literal_datetime(DateTime.parse(super))
|
198
|
-
else
|
199
|
-
super
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
204
|
ExtendedDateSupport::CONVERT_TYPES = [Java::JavaSQL::Types::DATE, Java::JavaSQL::Types::TIMESTAMP]
|
204
205
|
|
205
206
|
# Use non-JDBC parsing as JDBC parsing doesn't work for BC dates/timestamps.
|
@@ -211,6 +212,18 @@ module Sequel
|
|
211
212
|
super
|
212
213
|
end
|
213
214
|
end
|
215
|
+
|
216
|
+
# Work around JRuby bug #4822 in Time#to_datetime for times before date of calendar reform
|
217
|
+
def literal_time(time)
|
218
|
+
if time < TIME_YEAR_1
|
219
|
+
dt = DateTime.parse(super)
|
220
|
+
# Work around JRuby bug #5191
|
221
|
+
dt >>= 12 if JRUBY_VERSION == '9.2.0.0'
|
222
|
+
literal_datetime(dt)
|
223
|
+
else
|
224
|
+
super
|
225
|
+
end
|
226
|
+
end
|
214
227
|
# :nocov:
|
215
228
|
else
|
216
229
|
# Handle BC Time objects.
|
@@ -2751,7 +2751,7 @@ module Sequel
|
|
2751
2751
|
opts = @opts[:eager]
|
2752
2752
|
association_opts = eager_options_for_associations(associations)
|
2753
2753
|
opts = opts ? Hash[opts].merge!(association_opts) : association_opts
|
2754
|
-
clone(:eager=>opts)
|
2754
|
+
clone(:eager=>opts.freeze)
|
2755
2755
|
end
|
2756
2756
|
|
2757
2757
|
# The secondary eager loading method. Loads all associations in a single query. This
|
@@ -2800,8 +2800,9 @@ module Sequel
|
|
2800
2800
|
# significantly slower in some cases (perhaps even the majority of cases), so you should
|
2801
2801
|
# only use this if you have benchmarked that it is faster for your use cases.
|
2802
2802
|
def eager_graph_with_options(associations, opts=OPTS)
|
2803
|
+
opts = opts.dup unless opts.frozen?
|
2803
2804
|
associations = [associations] unless associations.is_a?(Array)
|
2804
|
-
if eg = @opts[:eager_graph]
|
2805
|
+
ds = if eg = @opts[:eager_graph]
|
2805
2806
|
eg = eg.dup
|
2806
2807
|
[:requirements, :reflections, :reciprocals, :limits].each{|k| eg[k] = eg[k].dup}
|
2807
2808
|
eg[:local] = opts
|
@@ -2815,8 +2816,12 @@ module Sequel
|
|
2815
2816
|
# :limits :: Any limit/offset array slicing that need to be handled in ruby land after loading
|
2816
2817
|
opts = {:requirements=>{}, :master=>alias_symbol(first_source), :reflections=>{}, :reciprocals=>{}, :limits=>{}, :local=>opts, :cartesian_product_number=>0, :row_proc=>row_proc}
|
2817
2818
|
ds = clone(:eager_graph=>opts)
|
2818
|
-
ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations).naked
|
2819
|
+
ds = ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations).naked
|
2819
2820
|
end
|
2821
|
+
|
2822
|
+
ds.opts[:eager_graph].freeze
|
2823
|
+
ds.opts[:eager_graph].each_value{|v| v.freeze if v.is_a?(Hash)}
|
2824
|
+
ds
|
2820
2825
|
end
|
2821
2826
|
|
2822
2827
|
# If the dataset is being eagerly loaded, default to calling all
|
@@ -3081,7 +3086,7 @@ module Sequel
|
|
3081
3086
|
associations = eager_assoc[r[:name]]
|
3082
3087
|
if associations.respond_to?(:call)
|
3083
3088
|
eager_block = associations
|
3084
|
-
associations =
|
3089
|
+
associations = OPTS
|
3085
3090
|
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
|
3086
3091
|
eager_block, associations = pr_assoc
|
3087
3092
|
end
|
@@ -3353,6 +3358,15 @@ module Sequel
|
|
3353
3358
|
key = hkey(ta_h)
|
3354
3359
|
end
|
3355
3360
|
rm, rp, assoc_name, tm, rcm = @ta_map[ta]
|
3361
|
+
|
3362
|
+
# Check type map for all dependencies, and use a unique
|
3363
|
+
# object if any are dependencies for multiple objects,
|
3364
|
+
# to prevent duplicate objects from showing up in the case
|
3365
|
+
# the normal duplicate removal code is not being used.
|
3366
|
+
if !@unique && !deps.empty? && deps.any?{|dep_key,_| @ta_map[dep_key][3]}
|
3367
|
+
key = [current.object_id, key]
|
3368
|
+
end
|
3369
|
+
|
3356
3370
|
unless rec = rm[key]
|
3357
3371
|
rec = rm[key] = rp.call(hfor(ta, h))
|
3358
3372
|
end
|
data/lib/sequel/model/base.rb
CHANGED
@@ -250,7 +250,7 @@ module Sequel
|
|
250
250
|
# Artist.create do |a|
|
251
251
|
# a.name = 'Jim'
|
252
252
|
# end # INSERT INTO artists (name) VALUES ('Jim')
|
253
|
-
def create(values =
|
253
|
+
def create(values = OPTS, &block)
|
254
254
|
new(values, &block).save
|
255
255
|
end
|
256
256
|
|
@@ -446,6 +446,13 @@ module Sequel
|
|
446
446
|
super
|
447
447
|
end
|
448
448
|
|
449
|
+
# Whether the model has a dataset. True for most model classes,
|
450
|
+
# but can be false if the model class is an abstract model class
|
451
|
+
# designed for subclassing, such as Sequel::Model itself.
|
452
|
+
def has_dataset?
|
453
|
+
!@dataset.nil?
|
454
|
+
end
|
455
|
+
|
449
456
|
# Clear the setter_methods cache when a module is included, as it
|
450
457
|
# may contain setter methods.
|
451
458
|
def include(*mods)
|
@@ -1055,7 +1062,7 @@ module Sequel
|
|
1055
1062
|
# Artist.new do |a|
|
1056
1063
|
# a.name = 'Bob'
|
1057
1064
|
# end
|
1058
|
-
def initialize(values =
|
1065
|
+
def initialize(values = OPTS)
|
1059
1066
|
@values = {}
|
1060
1067
|
@new = true
|
1061
1068
|
@modified = true
|
@@ -95,7 +95,7 @@ module Sequel
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
98
|
-
@default_values = h.merge!(@default_values ||
|
98
|
+
@default_values = h.merge!(@default_values || OPTS)
|
99
99
|
end
|
100
100
|
|
101
101
|
# Handle the CURRENT_DATE and CURRENT_TIMESTAMP values specially by returning an appropriate Date or
|
@@ -114,10 +114,10 @@ module Sequel
|
|
114
114
|
# If a block is provided, it is used to set the :reject_if option.
|
115
115
|
def nested_attributes(*associations, &block)
|
116
116
|
include(@nested_attributes_module ||= Module.new) unless @nested_attributes_module
|
117
|
-
opts = associations.last.is_a?(Hash) ? associations.pop :
|
117
|
+
opts = associations.last.is_a?(Hash) ? associations.pop : OPTS
|
118
118
|
reflections = associations.map{|a| association_reflection(a) || raise(Error, "no association named #{a} for #{self}")}
|
119
119
|
reflections.each do |r|
|
120
|
-
r[:nested_attributes] = opts
|
120
|
+
r[:nested_attributes] = opts.dup
|
121
121
|
r[:nested_attributes][:unmatched_pk] ||= :raise
|
122
122
|
r[:nested_attributes][:reject_if] ||= block
|
123
123
|
def_nested_attribute_method(r)
|
@@ -64,7 +64,7 @@ module Sequel
|
|
64
64
|
def self.configure(model, opts=OPTS)
|
65
65
|
model.instance_exec do
|
66
66
|
setup_pg_auto_constraint_validations
|
67
|
-
@pg_auto_constraint_validations_messages = (@pg_auto_constraint_validations_messages || DEFAULT_ERROR_MESSAGES).merge(opts[:messages] ||
|
67
|
+
@pg_auto_constraint_validations_messages = (@pg_auto_constraint_validations_messages || DEFAULT_ERROR_MESSAGES).merge(opts[:messages] || OPTS).freeze
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -195,7 +195,7 @@ module Sequel
|
|
195
195
|
end
|
196
196
|
end
|
197
197
|
end
|
198
|
-
rescue
|
198
|
+
rescue
|
199
199
|
# If there is an error trying to conver the constraint violation
|
200
200
|
# into a validation failure, it's best to just raise the constraint
|
201
201
|
# violation. This can make debugging the above block of code more
|
@@ -105,7 +105,6 @@ module Sequel
|
|
105
105
|
key_present = lambda{|m| key_conv[m].all?}
|
106
106
|
prkey_conv = lambda{|m| prkey_array.map{|k| m[k]}}
|
107
107
|
key_aliases = (0...key_array.length).map{|i| :"#{ka}_#{i}"}
|
108
|
-
ka_conv = lambda{|m| key_aliases.map{|k| m[k]}}
|
109
108
|
ancestor_base_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
|
110
109
|
descendant_base_case_columns = key_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
|
111
110
|
recursive_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::QualifiedIdentifier.new(t, ka_)} + c_all
|
@@ -114,18 +113,17 @@ module Sequel
|
|
114
113
|
key_present = key_conv = lambda{|m| m[key]}
|
115
114
|
prkey_conv = lambda{|m| m[prkey]}
|
116
115
|
key_aliases = [ka]
|
117
|
-
ka_conv = lambda{|m| m[ka]}
|
118
116
|
ancestor_base_case_columns = [SQL::AliasedExpression.new(prkey, ka)] + c_all
|
119
117
|
descendant_base_case_columns = [SQL::AliasedExpression.new(key, ka)] + c_all
|
120
118
|
recursive_case_columns = [SQL::QualifiedIdentifier.new(t, ka)] + c_all
|
121
119
|
extract_key_alias = lambda{|m| bd_conv[m.values.delete(ka)]}
|
122
120
|
end
|
123
121
|
|
124
|
-
parent = opts.merge(opts.fetch(:parent,
|
125
|
-
childrena = opts.merge(opts.fetch(:children,
|
122
|
+
parent = opts.merge(opts.fetch(:parent, OPTS)).fetch(:name, :parent)
|
123
|
+
childrena = opts.merge(opts.fetch(:children, OPTS)).fetch(:name, :children)
|
126
124
|
|
127
125
|
opts[:reciprocal] = nil
|
128
|
-
a = opts.merge(opts.fetch(:ancestors,
|
126
|
+
a = opts.merge(opts.fetch(:ancestors, OPTS))
|
129
127
|
ancestors = a.fetch(:name, :ancestors)
|
130
128
|
a[:read_only] = true unless a.has_key?(:read_only)
|
131
129
|
a[:eager_loader_key] = key
|
@@ -220,7 +218,7 @@ module Sequel
|
|
220
218
|
end
|
221
219
|
model.one_to_many ancestors, a
|
222
220
|
|
223
|
-
d = opts.merge(opts.fetch(:descendants,
|
221
|
+
d = opts.merge(opts.fetch(:descendants, OPTS))
|
224
222
|
descendants = d.fetch(:name, :descendants)
|
225
223
|
d[:read_only] = true unless d.has_key?(:read_only)
|
226
224
|
la = d[:level_alias] ||= :x_level_x
|
@@ -296,7 +294,7 @@ module Sequel
|
|
296
294
|
:args=>((key_aliases + col_aliases + (level ? [la] : [])) if col_aliases))
|
297
295
|
ds = r.apply_eager_dataset_changes(ds)
|
298
296
|
ds = ds.select_append(ka) unless ds.opts[:select] == nil
|
299
|
-
model.eager_load_results(r, eo.merge(:loader=>false, :initalize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>
|
297
|
+
model.eager_load_results(r, eo.merge(:loader=>false, :initalize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>OPTS)) do |obj|
|
300
298
|
if level
|
301
299
|
no_cache = no_cache_level == obj.values.delete(la)
|
302
300
|
end
|
@@ -21,7 +21,7 @@ module Sequel
|
|
21
21
|
module Sharding
|
22
22
|
module ClassMethods
|
23
23
|
# Create a new object on the given shard s.
|
24
|
-
def create_using_server(s, values=
|
24
|
+
def create_using_server(s, values=OPTS, &block)
|
25
25
|
new_using_server(s, values, &block).save
|
26
26
|
end
|
27
27
|
|
@@ -45,7 +45,7 @@ module Sequel
|
|
45
45
|
# Return a newly instantiated object that is tied to the given
|
46
46
|
# shard s. When the object is saved, a record will be inserted
|
47
47
|
# on shard s.
|
48
|
-
def new_using_server(s, values=
|
48
|
+
def new_using_server(s, values=OPTS, &block)
|
49
49
|
new(values, &block).set_server(s)
|
50
50
|
end
|
51
51
|
|