sequel 3.8.0 → 3.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +48 -0
- data/Rakefile +6 -28
- data/bin/sequel +7 -2
- data/doc/release_notes/3.9.0.txt +233 -0
- data/lib/sequel/adapters/ado.rb +4 -8
- data/lib/sequel/adapters/amalgalite.rb +1 -1
- data/lib/sequel/adapters/dbi.rb +3 -3
- data/lib/sequel/adapters/do.rb +7 -13
- data/lib/sequel/adapters/jdbc.rb +10 -16
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/mysql.rb +10 -23
- data/lib/sequel/adapters/odbc.rb +6 -10
- data/lib/sequel/adapters/postgres.rb +0 -5
- data/lib/sequel/adapters/shared/mssql.rb +17 -9
- data/lib/sequel/adapters/shared/mysql.rb +16 -7
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/adapters/sqlite.rb +2 -1
- data/lib/sequel/connection_pool.rb +67 -349
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +46 -15
- data/lib/sequel/database.rb +11 -9
- data/lib/sequel/dataset/convenience.rb +23 -0
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/query.rb +9 -5
- data/lib/sequel/dataset/sql.rb +87 -12
- data/lib/sequel/extensions/inflector.rb +8 -1
- data/lib/sequel/extensions/schema_dumper.rb +3 -4
- data/lib/sequel/model/associations.rb +5 -43
- data/lib/sequel/model/base.rb +9 -2
- data/lib/sequel/model/default_inflections.rb +1 -1
- data/lib/sequel/model/exceptions.rb +11 -1
- data/lib/sequel/model/inflections.rb +8 -1
- data/lib/sequel/model/plugins.rb +2 -12
- data/lib/sequel/plugins/active_model.rb +5 -0
- data/lib/sequel/plugins/association_dependencies.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/optimistic_locking.rb +65 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +14 -3
- data/lib/sequel/plugins/validation_helpers.rb +2 -2
- data/lib/sequel/sql.rb +2 -2
- data/lib/sequel/timezones.rb +2 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +19 -0
- data/spec/adapters/mysql_spec.rb +4 -0
- data/spec/adapters/postgres_spec.rb +180 -0
- data/spec/adapters/spec_helper.rb +15 -1
- data/spec/core/connection_pool_spec.rb +119 -78
- data/spec/core/database_spec.rb +41 -50
- data/spec/core/dataset_spec.rb +115 -4
- data/spec/extensions/active_model_spec.rb +40 -34
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/migration_spec.rb +43 -38
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/schema_dumper_spec.rb +4 -4
- data/spec/extensions/single_table_inheritance_spec.rb +19 -11
- data/spec/integration/dataset_test.rb +44 -1
- data/spec/integration/plugin_test.rb +39 -0
- data/spec/integration/prepared_statement_test.rb +58 -7
- data/spec/integration/spec_helper.rb +4 -0
- data/spec/model/eager_loading_spec.rb +24 -0
- data/spec/model/validations_spec.rb +5 -1
- 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
|
-
|
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
|
-
|
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
|
data/lib/sequel/database.rb
CHANGED
@@ -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 =
|
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.
|
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,
|
data/lib/sequel/dataset/graph.rb
CHANGED
@@ -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] = {}
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 = []
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -91,11 +91,42 @@ module Sequel
|
|
91
91
|
end
|
92
92
|
when :IN, :"NOT IN"
|
93
93
|
cols = args.at(0)
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
"#{
|
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
|
-
|
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
|
-
|
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.
|
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=>
|
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,
|
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.
|
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(*
|
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?
|