sequel 3.8.0 → 3.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 (65) hide show
  1. data/CHANGELOG +48 -0
  2. data/Rakefile +6 -28
  3. data/bin/sequel +7 -2
  4. data/doc/release_notes/3.9.0.txt +233 -0
  5. data/lib/sequel/adapters/ado.rb +4 -8
  6. data/lib/sequel/adapters/amalgalite.rb +1 -1
  7. data/lib/sequel/adapters/dbi.rb +3 -3
  8. data/lib/sequel/adapters/do.rb +7 -13
  9. data/lib/sequel/adapters/jdbc.rb +10 -16
  10. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  11. data/lib/sequel/adapters/mysql.rb +10 -23
  12. data/lib/sequel/adapters/odbc.rb +6 -10
  13. data/lib/sequel/adapters/postgres.rb +0 -5
  14. data/lib/sequel/adapters/shared/mssql.rb +17 -9
  15. data/lib/sequel/adapters/shared/mysql.rb +16 -7
  16. data/lib/sequel/adapters/shared/sqlite.rb +5 -0
  17. data/lib/sequel/adapters/sqlite.rb +2 -1
  18. data/lib/sequel/connection_pool.rb +67 -349
  19. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  20. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  21. data/lib/sequel/connection_pool/single.rb +29 -0
  22. data/lib/sequel/connection_pool/threaded.rb +150 -0
  23. data/lib/sequel/core.rb +46 -15
  24. data/lib/sequel/database.rb +11 -9
  25. data/lib/sequel/dataset/convenience.rb +23 -0
  26. data/lib/sequel/dataset/graph.rb +2 -2
  27. data/lib/sequel/dataset/query.rb +9 -5
  28. data/lib/sequel/dataset/sql.rb +87 -12
  29. data/lib/sequel/extensions/inflector.rb +8 -1
  30. data/lib/sequel/extensions/schema_dumper.rb +3 -4
  31. data/lib/sequel/model/associations.rb +5 -43
  32. data/lib/sequel/model/base.rb +9 -2
  33. data/lib/sequel/model/default_inflections.rb +1 -1
  34. data/lib/sequel/model/exceptions.rb +11 -1
  35. data/lib/sequel/model/inflections.rb +8 -1
  36. data/lib/sequel/model/plugins.rb +2 -12
  37. data/lib/sequel/plugins/active_model.rb +5 -0
  38. data/lib/sequel/plugins/association_dependencies.rb +1 -1
  39. data/lib/sequel/plugins/many_through_many.rb +1 -1
  40. data/lib/sequel/plugins/optimistic_locking.rb +65 -0
  41. data/lib/sequel/plugins/single_table_inheritance.rb +14 -3
  42. data/lib/sequel/plugins/validation_helpers.rb +2 -2
  43. data/lib/sequel/sql.rb +2 -2
  44. data/lib/sequel/timezones.rb +2 -2
  45. data/lib/sequel/version.rb +1 -1
  46. data/spec/adapters/mssql_spec.rb +19 -0
  47. data/spec/adapters/mysql_spec.rb +4 -0
  48. data/spec/adapters/postgres_spec.rb +180 -0
  49. data/spec/adapters/spec_helper.rb +15 -1
  50. data/spec/core/connection_pool_spec.rb +119 -78
  51. data/spec/core/database_spec.rb +41 -50
  52. data/spec/core/dataset_spec.rb +115 -4
  53. data/spec/extensions/active_model_spec.rb +40 -34
  54. data/spec/extensions/boolean_readers_spec.rb +1 -1
  55. data/spec/extensions/migration_spec.rb +43 -38
  56. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  57. data/spec/extensions/schema_dumper_spec.rb +4 -4
  58. data/spec/extensions/single_table_inheritance_spec.rb +19 -11
  59. data/spec/integration/dataset_test.rb +44 -1
  60. data/spec/integration/plugin_test.rb +39 -0
  61. data/spec/integration/prepared_statement_test.rb +58 -7
  62. data/spec/integration/spec_helper.rb +4 -0
  63. data/spec/model/eager_loading_spec.rb +24 -0
  64. data/spec/model/validations_spec.rb +5 -1
  65. metadata +114 -106
data/lib/sequel/core.rb CHANGED
@@ -17,18 +17,6 @@
17
17
  #
18
18
  # Sequel.sqlite('blog.db'){|db| puts db[:users].count}
19
19
  #
20
- # Sequel converts two digit years in Dates and DateTimes by default,
21
- # so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
22
- # as December 13, 1999. You can override this to treat those dates as
23
- # January 2nd, 0003 and December 13, 0099, respectively, by setting:
24
- #
25
- # Sequel.convert_two_digit_years = false
26
- #
27
- # Sequel can use either Time or DateTime for times returned from the
28
- # database. It defaults to Time. To change it to DateTime, use:
29
- #
30
- # Sequel.datetime_class = DateTime
31
- #
32
20
  # Sequel doesn't pay much attention to timezones by default, but you can set it
33
21
  # handle timezones if you want. There are three separate timezone settings:
34
22
  #
@@ -62,9 +50,42 @@ module Sequel
62
50
  @convert_two_digit_years = true
63
51
  @datetime_class = Time
64
52
  @virtual_row_instance_eval = true
53
+ @require_thread = nil
54
+
55
+ # Mutex used to protect file loading
56
+ @require_mutex = Mutex.new
65
57
 
66
58
  class << self
67
- attr_accessor :convert_two_digit_years, :datetime_class, :virtual_row_instance_eval
59
+ # Sequel converts two digit years in Dates and DateTimes by default,
60
+ # so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
61
+ # as December 13, 1999. You can override this to treat those dates as
62
+ # January 2nd, 0003 and December 13, 0099, respectively, by setting this to false.
63
+ attr_accessor :convert_two_digit_years
64
+
65
+ # Sequel can use either Time or DateTime for times returned from the
66
+ # database. It defaults to Time. To change it to DateTime, set this to DateTime.
67
+ attr_accessor :datetime_class
68
+
69
+ attr_accessor :virtual_row_instance_eval
70
+
71
+ # Alias to the standard version of require
72
+ alias k_require require
73
+
74
+ private
75
+
76
+ # Make thread safe requiring reentrant to prevent deadlocks.
77
+ def check_requiring_thread
78
+ t = Thread.current
79
+ return(yield) if @require_thread == t
80
+ @require_mutex.synchronize do
81
+ begin
82
+ @require_thread = t
83
+ yield
84
+ ensure
85
+ @require_thread = nil
86
+ end
87
+ end
88
+ end
68
89
  end
69
90
 
70
91
  # Returns true if the passed object could be a specifier of conditions, false otherwise.
@@ -117,7 +138,7 @@ module Sequel
117
138
  # Sequel.extension(:schema_dumper)
118
139
  # Sequel.extension(:pagination, :query)
119
140
  def self.extension(*extensions)
120
- require(extensions, 'extensions')
141
+ extensions.each{|e| tsk_require "sequel/extensions/#{e}"}
121
142
  end
122
143
 
123
144
  # Set the method to call on identifiers going into the database. This affects
@@ -158,7 +179,7 @@ module Sequel
158
179
  def self.quote_identifiers=(value)
159
180
  Database.quote_identifiers = value
160
181
  end
161
-
182
+
162
183
  # Require all given files which should be in the same or a subdirectory of
163
184
  # this file. If a subdir is given, assume all files are in that subdir.
164
185
  def self.require(files, subdir=nil)
@@ -207,6 +228,16 @@ module Sequel
207
228
  end
208
229
  end
209
230
 
231
+ # Same as Sequel.require, but wrapped in a mutex in order to be thread safe.
232
+ def self.ts_require(*args)
233
+ check_requiring_thread{require(*args)}
234
+ end
235
+
236
+ # Same as Kernel.require, but wrapped in a mutex in order to be thread safe.
237
+ def self.tsk_require(*args)
238
+ check_requiring_thread{k_require(*args)}
239
+ end
240
+
210
241
  # If the supplied block takes a single argument,
211
242
  # yield a new SQL::VirtualRow instance to the block
212
243
  # argument. Otherwise, evaluate the block in the context of a new
@@ -80,20 +80,22 @@ module Sequel
80
80
  # is given, it is used as the connection_proc for the ConnectionPool.
81
81
  def initialize(opts = {}, &block)
82
82
  @opts ||= opts
83
+ @opts = connection_pool_default_options.merge(@opts)
84
+ @loggers = Array(@opts[:logger]) + Array(@opts[:loggers])
85
+ @opts[:disconnection_proc] ||= proc{|conn| disconnect_connection(conn)}
86
+ block ||= proc{|server| connect(server)}
87
+ @opts[:servers] = {} if @opts[:servers].is_a?(String)
83
88
 
84
- @single_threaded = opts.include?(:single_threaded) ? typecast_value_boolean(opts[:single_threaded]) : @@single_threaded
89
+ @opts[:single_threaded] = @single_threaded = @opts.include?(:single_threaded) ? typecast_value_boolean(@opts[:single_threaded]) : @@single_threaded
85
90
  @schemas = {}
86
- @default_schema = opts.include?(:default_schema) ? opts[:default_schema] : default_schema_default
91
+ @default_schema = opts.include?(:default_schema) ? @opts[:default_schema] : default_schema_default
87
92
  @prepared_statements = {}
88
93
  @transactions = []
89
94
  @identifier_input_method = nil
90
95
  @identifier_output_method = nil
91
96
  @quote_identifiers = nil
92
- @pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
93
- @pool.connection_proc = proc{|server| connect(server)} unless block
94
- @pool.disconnection_proc = proc{|conn| disconnect_connection(conn)} unless opts[:disconnection_proc]
97
+ @pool = ConnectionPool.get_pool(@opts, &block)
95
98
 
96
- @loggers = Array(opts[:logger]) + Array(opts[:loggers])
97
99
  ::Sequel::DATABASES.push(self)
98
100
  end
99
101
 
@@ -108,7 +110,7 @@ module Sequel
108
110
  unless klass = ADAPTER_MAP[scheme]
109
111
  # attempt to load the adapter file
110
112
  begin
111
- Sequel.require "adapters/#{scheme}"
113
+ Sequel.tsk_require "sequel/adapters/#{scheme}"
112
114
  rescue LoadError => e
113
115
  raise Sequel.convert_exception_class(e, AdapterNotFound)
114
116
  end
@@ -139,7 +141,7 @@ module Sequel
139
141
  scheme = :dbi if scheme =~ /\Adbi-/
140
142
  c = adapter_class(scheme)
141
143
  uri_options = c.send(:uri_to_options, uri)
142
- uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.to_s.strip.empty?
144
+ uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
143
145
  uri_options.entries.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
144
146
  opts = uri_options.merge(opts)
145
147
  end
@@ -281,7 +283,7 @@ module Sequel
281
283
  end
282
284
 
283
285
  # Connects to the database. This method should be overridden by descendants.
284
- def connect
286
+ def connect(server)
285
287
  raise NotImplementedError, "#connect should be overridden by adapters"
286
288
  end
287
289
 
@@ -274,6 +274,29 @@ module Sequel
274
274
  end
275
275
  end
276
276
 
277
+ # Creates a unique table alias that hasn't already been used in the dataset.
278
+ # table_alias can be any type of object accepted by alias_symbol.
279
+ # The symbol returned will be the implicit alias in the argument,
280
+ # possibly appended with "_N" if the implicit alias has already been
281
+ # used, where N is an integer starting at 0 and increasing until an
282
+ # unused one is found.
283
+ def unused_table_alias(table_alias)
284
+ table_alias = alias_symbol(table_alias)
285
+ used_aliases = []
286
+ used_aliases += opts[:from].map{|t| alias_symbol(t)} if opts[:from]
287
+ used_aliases += opts[:join].map{|j| j.table_alias ? alias_alias_symbol(j.table_alias) : alias_symbol(j.table)} if opts[:join]
288
+ if used_aliases.include?(table_alias)
289
+ i = 0
290
+ loop do
291
+ ta = :"#{table_alias}_#{i}"
292
+ return ta unless used_aliases.include?(ta)
293
+ i += 1
294
+ end
295
+ else
296
+ table_alias
297
+ end
298
+ end
299
+
277
300
  private
278
301
 
279
302
  # Return a plain symbol given a potentially qualified or aliased symbol,
@@ -5,7 +5,7 @@ module Sequel
5
5
  # #set_graph_aliases.
6
6
  def add_graph_aliases(graph_aliases)
7
7
  ds = select_more(*graph_alias_columns(graph_aliases))
8
- ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || ds.opts[:graph][:column_aliases] || {}).merge(graph_aliases)
8
+ ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || (ds.opts[:graph][:column_aliases] rescue {}) || {}).merge(graph_aliases)
9
9
  ds
10
10
  end
11
11
 
@@ -102,7 +102,7 @@ module Sequel
102
102
 
103
103
  # Setup the initial graph data structure if it doesn't exist
104
104
  unless graph = opts[:graph]
105
- master = ds.first_source_alias
105
+ master = alias_symbol(ds.first_source_alias)
106
106
  raise_alias_error.call if master == table_alias
107
107
  # Master hash storing all .graph related information
108
108
  graph = opts[:graph] = {}
@@ -221,12 +221,16 @@ module Sequel
221
221
  o = l.first
222
222
  l = l.last - l.first + (l.exclude_end? ? 0 : 1)
223
223
  end
224
- l = l.to_i
225
- raise(Error, 'Limits must be greater than or equal to 1') unless l >= 1
224
+ l = l.to_i if l.is_a?(String) && !l.is_a?(LiteralString)
225
+ if l.is_a?(Integer)
226
+ raise(Error, 'Limits must be greater than or equal to 1') unless l >= 1
227
+ end
226
228
  opts = {:limit => l}
227
229
  if o
228
- o = o.to_i
229
- raise(Error, 'Offsets must be greater than or equal to 0') unless o >= 0
230
+ o = o.to_i if o.is_a?(String) && !o.is_a?(LiteralString)
231
+ if o.is_a?(Integer)
232
+ raise(Error, 'Offsets must be greater than or equal to 0') unless o >= 0
233
+ end
230
234
  opts[:offset] = o
231
235
  end
232
236
  clone(opts)
@@ -285,7 +289,7 @@ module Sequel
285
289
  #
286
290
  # dataset.select(:a) # SELECT a FROM items
287
291
  # dataset.select(:a, :b) # SELECT a, b FROM items
288
- # dataset.select{|o| o.a, o.sum(:b)} # SELECT a, sum(b) FROM items
292
+ # dataset.select{|o| [o.a, o.sum(:b)]} # SELECT a, sum(b) FROM items
289
293
  def select(*columns, &block)
290
294
  columns += Array(Sequel.virtual_row(&block)) if block
291
295
  m = []
@@ -91,11 +91,42 @@ module Sequel
91
91
  end
92
92
  when :IN, :"NOT IN"
93
93
  cols = args.at(0)
94
- if !supports_multiple_column_in? && cols.is_a?(Array)
95
- expr = SQL::BooleanExpression.new(:OR, *args.at(1).to_a.map{|vals| SQL::BooleanExpression.from_value_pairs(cols.zip(vals).map{|col, val| [col, val]})})
96
- literal(op == :IN ? expr : ~expr)
94
+ vals = args.at(1)
95
+ col_array = true if cols.is_a?(Array) || cols.is_a?(SQL::SQLArray)
96
+ if vals.is_a?(Array) || vals.is_a?(SQL::SQLArray)
97
+ val_array = true
98
+ empty_val_array = vals.to_a == []
99
+ end
100
+ if col_array
101
+ if empty_val_array
102
+ if op == :IN
103
+ literal(SQL::BooleanExpression.from_value_pairs(cols.to_a.map{|x| [x, x]}, :AND, true))
104
+ else
105
+ literal(1=>1)
106
+ end
107
+ elsif !supports_multiple_column_in?
108
+ if val_array
109
+ expr = SQL::BooleanExpression.new(:OR, *vals.to_a.map{|vs| SQL::BooleanExpression.from_value_pairs(cols.to_a.zip(vs).map{|c, v| [c, v]})})
110
+ literal(op == :IN ? expr : ~expr)
111
+ else
112
+ old_vals = vals
113
+ vals = vals.to_a
114
+ val_cols = old_vals.columns
115
+ complex_expression_sql(op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
116
+ end
117
+ else
118
+ "(#{literal(cols)} #{op} #{literal(vals)})"
119
+ end
97
120
  else
98
- "(#{literal(cols)} #{op} #{literal(args.at(1))})"
121
+ if empty_val_array
122
+ if op == :IN
123
+ literal(SQL::BooleanExpression.from_value_pairs([[cols, cols]], :AND, true))
124
+ else
125
+ literal(1=>1)
126
+ end
127
+ else
128
+ "(#{literal(cols)} #{op} #{literal(vals)})"
129
+ end
99
130
  end
100
131
  when *TWO_ARITY_OPERATORS
101
132
  "(#{literal(args.at(0))} #{op} #{literal(args.at(1))})"
@@ -288,13 +319,17 @@ module Sequel
288
319
  return join_table(type, table, h, options)
289
320
  end
290
321
 
291
- if [Symbol, String].any?{|c| options.is_a?(c)}
322
+ case options
323
+ when Hash
324
+ table_alias = options[:table_alias]
325
+ last_alias = options[:implicit_qualifier]
326
+ when Symbol, String, SQL::Identifier
292
327
  table_alias = options
293
328
  last_alias = nil
294
329
  else
295
- table_alias = options[:table_alias]
296
- last_alias = options[:implicit_qualifier]
330
+ raise Error, "invalid options format for join_table: #{options.inspect}"
297
331
  end
332
+
298
333
  if Dataset === table
299
334
  if table_alias.nil?
300
335
  table_alias_num = (@opts[:num_dataset_sources] || 0) + 1
@@ -360,7 +395,7 @@ module Sequel
360
395
  when BigDecimal
361
396
  literal_big_decimal(v)
362
397
  when NilClass
363
- NULL
398
+ literal_nil
364
399
  when TrueClass
365
400
  literal_true
366
401
  when FalseClass
@@ -630,6 +665,41 @@ module Sequel
630
665
  "TRUNCATE TABLE #{table}"
631
666
  end
632
667
 
668
+ # Returns an appropriate symbol for the alias represented by s.
669
+ def alias_alias_symbol(s)
670
+ case s
671
+ when Symbol
672
+ s
673
+ when String
674
+ s.to_sym
675
+ when SQL::Identifier
676
+ s.value.to_s.to_sym
677
+ else
678
+ raise Error, "Invalid alias for alias_alias_symbol: #{s.inspect}"
679
+ end
680
+ end
681
+
682
+ # Returns an appropriate alias symbol for the given object, which can be
683
+ # a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or
684
+ # SQL::AliasedExpression.
685
+ def alias_symbol(sym)
686
+ case sym
687
+ when Symbol
688
+ s, t, a = split_symbol(sym)
689
+ a || s ? (a || t).to_sym : sym
690
+ when String
691
+ sym.to_sym
692
+ when SQL::Identifier
693
+ sym.value.to_s.to_sym
694
+ when SQL::QualifiedIdentifier
695
+ alias_symbol(sym.column)
696
+ when SQL::AliasedExpression
697
+ alias_alias_symbol(sym.aliaz)
698
+ else
699
+ raise Error, "Invalid alias for alias_symbol: #{sym.inspect}"
700
+ end
701
+ end
702
+
633
703
  # Clone of this dataset usable in aggregate operations. Does
634
704
  # a from_self if dataset contains any parameters that would
635
705
  # affect normal aggregation, or just removes an existing
@@ -791,7 +861,7 @@ module Sequel
791
861
 
792
862
  # SQL fragment for Date, using the ISO8601 format.
793
863
  def literal_date(v)
794
- requires_sql_standard_datetimes? ? v.strftime("DATE '%Y-%m-%d'") : "'#{v}'"
864
+ v.strftime("#{'DATE ' if requires_sql_standard_datetimes?}'%Y-%m-%d'")
795
865
  end
796
866
 
797
867
  # SQL fragment for DateTime
@@ -823,6 +893,11 @@ module Sequel
823
893
  def literal_integer(v)
824
894
  v.to_s
825
895
  end
896
+
897
+ # SQL fragment for nil
898
+ def literal_nil
899
+ NULL
900
+ end
826
901
 
827
902
  # SQL fragment for a type of object not handled by Dataset#literal.
828
903
  # Calls sql_literal if object responds to it, otherwise raises an error.
@@ -996,8 +1071,8 @@ module Sequel
996
1071
 
997
1072
  # Modify the sql to limit the number of rows returned and offset
998
1073
  def select_limit_sql(sql)
999
- sql << " LIMIT #{@opts[:limit]}" if @opts[:limit]
1000
- sql << " OFFSET #{@opts[:offset]}" if @opts[:offset]
1074
+ sql << " LIMIT #{literal(@opts[:limit])}" if @opts[:limit]
1075
+ sql << " OFFSET #{literal(@opts[:offset])}" if @opts[:offset]
1001
1076
  end
1002
1077
 
1003
1078
  # Modify the sql to add the expressions to ORDER BY
@@ -1087,7 +1162,7 @@ module Sequel
1087
1162
  values = values.merge(opts[:overrides]) if opts[:overrides]
1088
1163
  # get values from hash
1089
1164
  values.map do |k, v|
1090
- "#{[String, Symbol].any?{|c| k.is_a?(c)} ? quote_identifier(k) : literal(k)} = #{literal(v)}"
1165
+ "#{k.is_a?(String) && !k.is_a?(LiteralString) ? quote_identifier(k) : literal(k)} = #{literal(v)}"
1091
1166
  end.join(COMMA_SEPARATOR)
1092
1167
  else
1093
1168
  # copy values verbatim
@@ -23,7 +23,14 @@ class String
23
23
  @plurals, @singulars, @uncountables = [], [], []
24
24
 
25
25
  class << self
26
- attr_reader :plurals, :singulars, :uncountables
26
+ # Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for plurization.
27
+ attr_reader :plurals
28
+
29
+ # Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for singularization.
30
+ attr_reader :singulars
31
+
32
+ # Array of strings for words were the singular form is the same as the plural form
33
+ attr_reader :uncountables
27
34
  end
28
35
 
29
36
  # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
@@ -11,7 +11,7 @@ module Sequel
11
11
  # * :same_db - Create a dump for the same database type, so
12
12
  # don't ignore errors if the index statements fail.
13
13
  def dump_indexes_migration(options={})
14
- ts = tables
14
+ ts = tables(options)
15
15
  <<END_MIG
16
16
  Class.new(Sequel::Migration) do
17
17
  def up
@@ -35,7 +35,7 @@ END_MIG
35
35
  # * :indexes - If set to false, don't dump indexes (they can be added
36
36
  # later via dump_index_migration).
37
37
  def dump_schema_migration(options={})
38
- ts = tables
38
+ ts = tables(options)
39
39
  <<END_MIG
40
40
  Class.new(Sequel::Migration) do
41
41
  def up
@@ -135,8 +135,7 @@ END_MIG
135
135
  when /\An?char(?:acter)?(?:\((\d+)\))?\z/o
136
136
  {:type=>String, :size=>($1.to_i if $1), :fixed=>true}
137
137
  when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/o
138
- s = ($1.to_i if $1)
139
- {:type=>String, :size=>(s == 255 ? nil : s)}
138
+ {:type=>String, :size=>($1.to_i if $1)}
140
139
  when /\A(?:small)?money\z/o
141
140
  {:type=>BigDecimal, :size=>[19,2]}
142
141
  when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/o
@@ -763,7 +763,7 @@ module Sequel
763
763
  jt_join_type = opts[:graph_join_table_join_type]
764
764
  jt_graph_block = opts[:graph_join_table_block]
765
765
  opts[:eager_grapher] ||= proc do |ds, assoc_alias, table_alias|
766
- ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(lcpks) + graph_jt_conds, :select=>false, :table_alias=>ds.send(:eager_unique_table_alias, ds, join_table), :join_type=>jt_join_type, :implicit_qualifier=>table_alias, :from_self_alias=>ds.opts[:eager_graph][:master], &jt_graph_block)
766
+ ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(lcpks) + graph_jt_conds, :select=>false, :table_alias=>ds.unused_table_alias(join_table), :join_type=>jt_join_type, :implicit_qualifier=>table_alias, :from_self_alias=>ds.opts[:eager_graph][:master], &jt_graph_block)
767
767
  ds.graph(opts.associated_class, use_only_conditions ? only_conditions : opts.right_primary_keys.zip(rcks) + conditions, :select=>select, :table_alias=>assoc_alias, :join_type=>join_type, &graph_block)
768
768
  end
769
769
 
@@ -1213,8 +1213,6 @@ module Sequel
1213
1213
  # Like eager, you need to call .all on the dataset for the eager loading to work. If you just
1214
1214
  # call each, you will get a normal graphed result back (a hash with model object values).
1215
1215
  def eager_graph(*associations)
1216
- table_name = model.table_name
1217
-
1218
1216
  ds = if @opts[:eager_graph]
1219
1217
  self
1220
1218
  else
@@ -1223,9 +1221,9 @@ module Sequel
1223
1221
  # :requirements - array of requirements for this association
1224
1222
  # :alias_association_type_map - the type of association for this association
1225
1223
  # :alias_association_name_map - the name of the association for this association
1226
- clone(:eager_graph=>{:requirements=>{}, :master=>table_name, :alias_association_type_map=>{}, :alias_association_name_map=>{}, :reciprocals=>{}, :cartesian_product_number=>0})
1224
+ clone(:eager_graph=>{:requirements=>{}, :master=>alias_symbol(first_source), :alias_association_type_map=>{}, :alias_association_name_map=>{}, :reciprocals=>{}, :cartesian_product_number=>0})
1227
1225
  end
1228
- ds.eager_graph_associations(ds, model, table_name, [], *associations)
1226
+ ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations)
1229
1227
  end
1230
1228
 
1231
1229
  # Do not attempt to split the result set into associations,
@@ -1253,9 +1251,9 @@ module Sequel
1253
1251
  def eager_graph_association(ds, model, ta, requirements, r, *associations)
1254
1252
  klass = r.associated_class
1255
1253
  assoc_name = r[:name]
1256
- assoc_table_alias = ds.eager_unique_table_alias(ds, assoc_name)
1254
+ assoc_table_alias = ds.unused_table_alias(assoc_name)
1257
1255
  ds = r[:eager_grapher].call(ds, assoc_table_alias, ta)
1258
- ds = ds.order_more(*Array(r[:order]).map{|c| eager_graph_qualify_order(assoc_table_alias, c)}) if r[:order] and r[:order_eager_graph]
1256
+ ds = ds.order_more(*qualified_expression(r[:order], assoc_table_alias)) if r[:order] and r[:order_eager_graph]
1259
1257
  eager_graph = ds.opts[:eager_graph]
1260
1258
  eager_graph[:requirements][assoc_table_alias] = requirements.dup
1261
1259
  eager_graph[:alias_association_name_map][assoc_table_alias] = assoc_name
@@ -1354,25 +1352,6 @@ module Sequel
1354
1352
  record_graphs.replace(records)
1355
1353
  end
1356
1354
 
1357
- # Creates a unique table alias that hasn't already been used in the query.
1358
- # Will either be the table_alias itself or table_alias_N for some integer
1359
- # N (starting at 0 and increasing until an unused one is found).
1360
- def eager_unique_table_alias(ds, table_alias)
1361
- used_aliases = ds.opts[:from]
1362
- used_aliases += ds.opts[:join].map{|j| j.table_alias || j.table} if ds.opts[:join]
1363
- graph = ds.opts[:graph]
1364
- used_aliases += graph[:table_aliases].keys if graph
1365
- if used_aliases.include?(table_alias)
1366
- i = 0
1367
- loop do
1368
- ta = :"#{table_alias}_#{i}"
1369
- return ta unless used_aliases.include?(ta)
1370
- i += 1
1371
- end
1372
- end
1373
- table_alias
1374
- end
1375
-
1376
1355
  private
1377
1356
 
1378
1357
  # Make sure the association is valid for this model, and return the related AssociationReflection.
@@ -1433,23 +1412,6 @@ module Sequel
1433
1412
  end
1434
1413
  end
1435
1414
 
1436
- # Qualify the given expression if necessary. The only expressions which are qualified are
1437
- # unqualified symbols and identifiers, either of which may by sorted.
1438
- def eager_graph_qualify_order(table_alias, expression)
1439
- case expression
1440
- when Symbol
1441
- table, column, aliaz = split_symbol(expression)
1442
- raise(Sequel::Error, "Can't use an aliased expression in the :order option") if aliaz
1443
- table ? expression : Sequel::SQL::QualifiedIdentifier.new(table_alias, expression)
1444
- when Sequel::SQL::Identifier
1445
- Sequel::SQL::QualifiedIdentifier.new(table_alias, expression)
1446
- when Sequel::SQL::OrderedExpression
1447
- Sequel::SQL::OrderedExpression.new(eager_graph_qualify_order(table_alias, expression.expression), expression.descending)
1448
- else
1449
- expression
1450
- end
1451
- end
1452
-
1453
1415
  # Eagerly load all specified associations
1454
1416
  def eager_load(a, eager_assoc=@opts[:eager])
1455
1417
  return if a.empty?