sequel 5.8.0 → 5.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +30 -0
  3. data/doc/release_notes/5.9.0.txt +99 -0
  4. data/doc/testing.rdoc +10 -10
  5. data/lib/sequel/adapters/ado.rb +1 -1
  6. data/lib/sequel/adapters/amalgalite.rb +1 -1
  7. data/lib/sequel/adapters/jdbc.rb +19 -7
  8. data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -5
  10. data/lib/sequel/adapters/postgres.rb +3 -3
  11. data/lib/sequel/adapters/shared/access.rb +5 -6
  12. data/lib/sequel/adapters/shared/mysql.rb +28 -2
  13. data/lib/sequel/adapters/shared/postgres.rb +16 -6
  14. data/lib/sequel/adapters/shared/sqlite.rb +1 -1
  15. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  16. data/lib/sequel/adapters/sqlite.rb +2 -2
  17. data/lib/sequel/connection_pool.rb +2 -1
  18. data/lib/sequel/connection_pool/sharded_threaded.rb +12 -4
  19. data/lib/sequel/connection_pool/threaded.rb +19 -7
  20. data/lib/sequel/core.rb +1 -1
  21. data/lib/sequel/database/connecting.rb +6 -6
  22. data/lib/sequel/database/misc.rb +3 -3
  23. data/lib/sequel/database/query.rb +2 -2
  24. data/lib/sequel/database/schema_generator.rb +9 -3
  25. data/lib/sequel/database/schema_methods.rb +12 -5
  26. data/lib/sequel/dataset/features.rb +5 -0
  27. data/lib/sequel/dataset/misc.rb +1 -1
  28. data/lib/sequel/dataset/prepared_statements.rb +4 -4
  29. data/lib/sequel/dataset/query.rb +5 -0
  30. data/lib/sequel/dataset/sql.rb +8 -6
  31. data/lib/sequel/extensions/escaped_like.rb +100 -0
  32. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  33. data/lib/sequel/extensions/looser_typecasting.rb +3 -3
  34. data/lib/sequel/extensions/pg_extended_date_support.rb +23 -10
  35. data/lib/sequel/model/associations.rb +18 -4
  36. data/lib/sequel/model/base.rb +9 -2
  37. data/lib/sequel/plugins/defaults_setter.rb +1 -1
  38. data/lib/sequel/plugins/many_through_many.rb +1 -1
  39. data/lib/sequel/plugins/nested_attributes.rb +2 -2
  40. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -2
  41. data/lib/sequel/plugins/rcte_tree.rb +5 -7
  42. data/lib/sequel/plugins/sharding.rb +2 -2
  43. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  44. data/lib/sequel/plugins/tree.rb +2 -2
  45. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  46. data/lib/sequel/sql.rb +2 -2
  47. data/lib/sequel/version.rb +4 -1
  48. data/spec/adapters/mysql_spec.rb +24 -0
  49. data/spec/adapters/postgres_spec.rb +9 -9
  50. data/spec/adapters/sqlite_spec.rb +10 -10
  51. data/spec/core/connection_pool_spec.rb +22 -0
  52. data/spec/core/database_spec.rb +6 -6
  53. data/spec/core/dataset_spec.rb +16 -5
  54. data/spec/core/expression_filters_spec.rb +1 -1
  55. data/spec/core/schema_spec.rb +1 -1
  56. data/spec/core/version_spec.rb +7 -0
  57. data/spec/extensions/connection_expiration_spec.rb +20 -2
  58. data/spec/extensions/connection_validator_spec.rb +20 -3
  59. data/spec/extensions/escaped_like_spec.rb +40 -0
  60. data/spec/extensions/eval_inspect_spec.rb +1 -1
  61. data/spec/extensions/nested_attributes_spec.rb +6 -0
  62. data/spec/extensions/pg_array_spec.rb +13 -13
  63. data/spec/extensions/pg_auto_constraint_validations_spec.rb +0 -1
  64. data/spec/extensions/pg_range_spec.rb +1 -1
  65. data/spec/extensions/schema_dumper_spec.rb +2 -2
  66. data/spec/extensions/sql_expr_spec.rb +1 -1
  67. data/spec/extensions/string_agg_spec.rb +1 -1
  68. data/spec/extensions/timestamps_spec.rb +2 -2
  69. data/spec/extensions/validation_helpers_spec.rb +1 -1
  70. data/spec/integration/associations_test.rb +12 -0
  71. data/spec/integration/dataset_test.rb +21 -0
  72. data/spec/integration/type_test.rb +4 -4
  73. data/spec/model/base_spec.rb +9 -0
  74. data/spec/model/eager_loading_spec.rb +25 -0
  75. data/spec/model/record_spec.rb +1 -1
  76. metadata +6 -2
@@ -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?
@@ -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
- sql << " ESCAPE "
393
- literal_append(sql, "\\")
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
- loop do
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
- loop do
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 array"
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 Sequel::SQL::Blob, Sequel::LiteralString, BigDecimal
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.new(value)
39
+ BigDecimal(value)
40
40
  rescue
41
- BigDecimal.new('0.0')
41
+ BigDecimal('0.0')
42
42
  end
43
43
  else
44
44
  # :nocov:
45
45
  def _typecast_value_string_to_decimal(value)
46
- BigDecimal.new(value)
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
@@ -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 = {}, &block)
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
@@ -144,7 +144,7 @@ module Sequel
144
144
  table_alias = e[:table]
145
145
  if aliases.include?(table_alias)
146
146
  i = 0
147
- table_alias = loop do
147
+ table_alias = while true
148
148
  ta = :"#{table_alias}_#{i}"
149
149
  break ta unless aliases.include?(ta)
150
150
  i += 1
@@ -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] || {}).freeze
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 => e2
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, {})).fetch(:name, :parent)
125
- childrena = opts.merge(opts.fetch(:children, {})).fetch(:name, :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=>{})) do |obj|
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={}, &block)
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={}, &block)
48
+ def new_using_server(s, values=OPTS, &block)
49
49
  new(values, &block).set_server(s)
50
50
  end
51
51