sequel 5.8.0 → 5.9.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.
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