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.
- 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?
|