sequel 4.8.0 → 4.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.
- checksums.yaml +4 -4
- data/CHANGELOG +48 -0
- data/doc/association_basics.rdoc +1 -1
- data/doc/opening_databases.rdoc +4 -0
- data/doc/postgresql.rdoc +27 -3
- data/doc/release_notes/4.9.0.txt +190 -0
- data/doc/security.rdoc +1 -1
- data/doc/testing.rdoc +2 -2
- data/doc/validations.rdoc +8 -0
- data/lib/sequel/adapters/jdbc.rb +5 -3
- data/lib/sequel/adapters/jdbc/derby.rb +2 -8
- data/lib/sequel/adapters/jdbc/h2.rb +2 -13
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -16
- data/lib/sequel/adapters/mysql2.rb +11 -1
- data/lib/sequel/adapters/postgres.rb +33 -10
- data/lib/sequel/adapters/shared/db2.rb +2 -10
- data/lib/sequel/adapters/shared/mssql.rb +10 -8
- data/lib/sequel/adapters/shared/oracle.rb +9 -24
- data/lib/sequel/adapters/shared/postgres.rb +32 -9
- data/lib/sequel/adapters/shared/sqlanywhere.rb +2 -4
- data/lib/sequel/adapters/shared/sqlite.rb +4 -7
- data/lib/sequel/database/schema_methods.rb +15 -0
- data/lib/sequel/dataset.rb +1 -1
- data/lib/sequel/dataset/actions.rb +159 -27
- data/lib/sequel/dataset/graph.rb +29 -7
- data/lib/sequel/dataset/misc.rb +6 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +164 -0
- data/lib/sequel/dataset/query.rb +2 -0
- data/lib/sequel/dataset/sql.rb +103 -91
- data/lib/sequel/extensions/current_datetime_timestamp.rb +57 -0
- data/lib/sequel/extensions/pg_array.rb +68 -106
- data/lib/sequel/extensions/pg_hstore.rb +5 -5
- data/lib/sequel/extensions/schema_dumper.rb +49 -49
- data/lib/sequel/model.rb +4 -2
- data/lib/sequel/model/associations.rb +1 -1
- data/lib/sequel/model/base.rb +136 -3
- data/lib/sequel/model/errors.rb +6 -0
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/eager_each.rb +9 -0
- data/lib/sequel/plugins/nested_attributes.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -2
- data/lib/sequel/plugins/touch.rb +2 -2
- data/lib/sequel/sql.rb +20 -15
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +70 -8
- data/spec/core/dataset_spec.rb +172 -27
- data/spec/core/expression_filters_spec.rb +3 -3
- data/spec/core/object_graph_spec.rb +17 -1
- data/spec/core/placeholder_literalizer_spec.rb +128 -0
- data/spec/core/schema_spec.rb +54 -0
- data/spec/extensions/current_datetime_timestamp_spec.rb +27 -0
- data/spec/extensions/defaults_setter_spec.rb +12 -0
- data/spec/extensions/eager_each_spec.rb +6 -0
- data/spec/extensions/nested_attributes_spec.rb +14 -2
- data/spec/extensions/pg_array_spec.rb +15 -7
- data/spec/extensions/shared_caching_spec.rb +5 -5
- data/spec/extensions/timestamps_spec.rb +9 -0
- data/spec/extensions/touch_spec.rb +9 -0
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +27 -5
- data/spec/model/eager_loading_spec.rb +32 -0
- data/spec/model/model_spec.rb +119 -9
- metadata +8 -2
data/lib/sequel/dataset/graph.rb
CHANGED
@@ -90,11 +90,18 @@ module Sequel
|
|
90
90
|
# Only allow table aliases that haven't been used
|
91
91
|
raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
|
92
92
|
|
93
|
+
table_alias_qualifier = qualifier_from_alias_symbol(table_alias, table)
|
94
|
+
implicit_qualifier = options[:implicit_qualifier]
|
95
|
+
ds = self
|
96
|
+
|
93
97
|
# Use a from_self if this is already a joined table (or from_self specifically disabled for graphs)
|
94
|
-
|
98
|
+
if (@opts[:graph_from_self] != false && !@opts[:graph] && (@opts[:from].length > 1 || @opts[:join]))
|
99
|
+
implicit_qualifier = options[:from_self_alias] || first_source
|
100
|
+
ds = ds.from_self(:alias=>implicit_qualifier)
|
101
|
+
end
|
95
102
|
|
96
103
|
# Join the table early in order to avoid cloning the dataset twice
|
97
|
-
ds = ds.join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>
|
104
|
+
ds = ds.join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias_qualifier, :implicit_qualifier=>implicit_qualifier, :qualify=>options[:qualify], &block)
|
98
105
|
opts = ds.opts
|
99
106
|
|
100
107
|
# Whether to include the table in the result set
|
@@ -108,7 +115,8 @@ module Sequel
|
|
108
115
|
select = opts[:select].dup
|
109
116
|
[:column_aliases, :table_aliases, :column_alias_num].each{|k| graph[k] = graph[k].dup}
|
110
117
|
else
|
111
|
-
|
118
|
+
qualifier = ds.first_source_alias
|
119
|
+
master = alias_symbol(qualifier)
|
112
120
|
raise_alias_error.call if master == table_alias
|
113
121
|
# Master hash storing all .graph related information
|
114
122
|
graph = opts[:graph] = {}
|
@@ -143,11 +151,11 @@ module Sequel
|
|
143
151
|
end
|
144
152
|
column_aliases[column] = [master, column]
|
145
153
|
end
|
146
|
-
select = qualified_expression(select,
|
154
|
+
select = qualified_expression(select, qualifier)
|
147
155
|
else
|
148
156
|
select = columns.map do |column|
|
149
157
|
column_aliases[column] = [master, column]
|
150
|
-
SQL::QualifiedIdentifier.new(
|
158
|
+
SQL::QualifiedIdentifier.new(qualifier, column)
|
151
159
|
end
|
152
160
|
end
|
153
161
|
end
|
@@ -179,9 +187,9 @@ module Sequel
|
|
179
187
|
column_alias = :"#{column_alias}_#{column_alias_num}"
|
180
188
|
ca_num[column_alias] += 1
|
181
189
|
end
|
182
|
-
[column_alias, SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(
|
190
|
+
[column_alias, SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table_alias_qualifier, column), column_alias)]
|
183
191
|
else
|
184
|
-
ident = SQL::QualifiedIdentifier.new(
|
192
|
+
ident = SQL::QualifiedIdentifier.new(table_alias_qualifier, column)
|
185
193
|
[column, ident]
|
186
194
|
end
|
187
195
|
column_aliases[col_alias] = [table_alias, column]
|
@@ -229,6 +237,20 @@ module Sequel
|
|
229
237
|
|
230
238
|
private
|
231
239
|
|
240
|
+
# Wrap the alias symbol in an SQL::Identifier if the identifier on which is based
|
241
|
+
# is an SQL::Identifier. This works around cases where the alias symbol contains
|
242
|
+
# double embedded underscores which would be considered an implicit qualified identifier
|
243
|
+
# if not wrapped in an SQL::Identifier.
|
244
|
+
def qualifier_from_alias_symbol(aliaz, identifier)
|
245
|
+
identifier = identifier.column if identifier.is_a?(SQL::QualifiedIdentifier)
|
246
|
+
case identifier
|
247
|
+
when SQL::Identifier
|
248
|
+
Sequel.identifier(aliaz)
|
249
|
+
else
|
250
|
+
aliaz
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
232
254
|
# Transform the hash of graph aliases and return a two element array
|
233
255
|
# where the first element is an array of identifiers suitable to pass to
|
234
256
|
# a select method, and the second is a new hash of preprocessed graph aliases.
|
data/lib/sequel/dataset/misc.rb
CHANGED
@@ -36,6 +36,12 @@ module Sequel
|
|
36
36
|
o.is_a?(self.class) && db == o.db && opts == o.opts && sql == o.sql
|
37
37
|
end
|
38
38
|
|
39
|
+
# An object representing the current date or time, should be an instance
|
40
|
+
# of Sequel.datetime_class.
|
41
|
+
def current_datetime
|
42
|
+
Sequel.datetime_class.now
|
43
|
+
end
|
44
|
+
|
39
45
|
# Alias for ==
|
40
46
|
def eql?(o)
|
41
47
|
self == o
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Sequel
|
2
|
+
class Dataset
|
3
|
+
# PlaceholderLiteralizer allows you to record the application of arbitrary changes
|
4
|
+
# to a dataset with placeholder arguments, recording where those placeholder arguments
|
5
|
+
# are used in the query. When running the query, the literalization process is much
|
6
|
+
# faster as Sequel can skip most of the work it normal has to do when literalizing a
|
7
|
+
# dataset.
|
8
|
+
#
|
9
|
+
# Basically, this enables optimizations that allow Sequel to cache the SQL produced
|
10
|
+
# for a given dataset, so that it doesn't need to recompute that information every
|
11
|
+
# time.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
|
16
|
+
# ds.where(:id=>pl.arg).exclude(:name=>pl.arg).limit(1)
|
17
|
+
# end
|
18
|
+
# loader.first(1, "foo")
|
19
|
+
# # SELECT * FROM items WHERE ((id = 1) AND (name != 'foo')) LIMIT 1
|
20
|
+
# loader.first(2, "bar")
|
21
|
+
# # SELECT * FROM items WHERE ((id = 2) AND (name != 'bar')) LIMIT 1
|
22
|
+
#
|
23
|
+
# Caveats:
|
24
|
+
#
|
25
|
+
# Note that this method does not handle all possible cases. For example:
|
26
|
+
#
|
27
|
+
# loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
|
28
|
+
# ds.join(pl.arg, :item_id=>:id)
|
29
|
+
# end
|
30
|
+
# loader(:cart_items)
|
31
|
+
#
|
32
|
+
# Will not qualify the item_id column with cart_items. In this type of situation it's
|
33
|
+
# best to add a table alias when joining:
|
34
|
+
#
|
35
|
+
# loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
|
36
|
+
# ds.join(Sequel.as(pl.arg, :t), :item_id=>:id)
|
37
|
+
# end
|
38
|
+
# loader(:cart_items)
|
39
|
+
#
|
40
|
+
# There are other similar cases that are not handled, mainly when Sequel changes the
|
41
|
+
# SQL produced depending on the types of the arguments.
|
42
|
+
class PlaceholderLiteralizer
|
43
|
+
# A placeholder argument used by the PlaceholderLiteralizer. This records the offset
|
44
|
+
# that the argument should be used in the resulting SQL.
|
45
|
+
class Argument
|
46
|
+
# Set the recorder, the argument position, and any transforming block to use
|
47
|
+
# for this placeholder.
|
48
|
+
def initialize(recorder, pos, transformer=nil)
|
49
|
+
@recorder = recorder
|
50
|
+
@pos = pos
|
51
|
+
@transformer = transformer
|
52
|
+
end
|
53
|
+
|
54
|
+
# Record the SQL query offset, argument position, and transforming block where the
|
55
|
+
# argument should be literalized.
|
56
|
+
def sql_literal_append(ds, sql)
|
57
|
+
@recorder.use(sql, @pos, @transformer)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return a new Argument object for the same recorder and argument position, but with a
|
61
|
+
# different transformer block.
|
62
|
+
def transform(&block)
|
63
|
+
Argument.new(@recorder, @pos, block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Records the offsets at which the placeholder arguments are used in
|
68
|
+
# the SQL query.
|
69
|
+
class Recorder
|
70
|
+
# Yields the receiver and the dataset to the block, which should
|
71
|
+
# call #arg on the receiver for each placeholder argument, and
|
72
|
+
# return the dataset that you want to load.
|
73
|
+
def loader(dataset)
|
74
|
+
@argn = -1
|
75
|
+
@args = []
|
76
|
+
ds = yield self, dataset
|
77
|
+
sql = ds.sql
|
78
|
+
|
79
|
+
last_offset = 0
|
80
|
+
fragments = @args.map do |used_sql, offset, arg, t|
|
81
|
+
raise Error, "placeholder literalizer argument literalized into different string than dataset returned" unless used_sql.equal?(sql)
|
82
|
+
a = [sql[last_offset...offset], arg, t]
|
83
|
+
last_offset = offset
|
84
|
+
a
|
85
|
+
end
|
86
|
+
final_sql = sql[last_offset..-1]
|
87
|
+
|
88
|
+
arity = @argn+1
|
89
|
+
PlaceholderLiteralizer.new(ds.clone, fragments, final_sql, arity)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Return an Argument with the specified position, or the next position. In
|
93
|
+
# general you shouldn't mix calls with an argument and calls without an
|
94
|
+
# argument for the same receiver.
|
95
|
+
def arg(v=(no_arg_given = true; @argn+=1))
|
96
|
+
unless no_arg_given
|
97
|
+
@argn = v if @argn < v
|
98
|
+
end
|
99
|
+
Argument.new(self, v)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Record the offset at which the argument is used in the SQL query, and any
|
103
|
+
# transforming
|
104
|
+
def use(sql, arg, transformer)
|
105
|
+
@args << [sql, sql.length, arg, transformer]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Create a PlaceholderLiteralizer by yielding a Recorder and dataset to the
|
110
|
+
# given block, recording the offsets at which the recorders arguments
|
111
|
+
# are used in the query.
|
112
|
+
def self.loader(dataset, &block)
|
113
|
+
Recorder.new.loader(dataset, &block)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Save the dataset, array of SQL fragments, and ending SQL string.
|
117
|
+
def initialize(dataset, fragments, final_sql, arity)
|
118
|
+
@dataset = dataset
|
119
|
+
@fragments = fragments
|
120
|
+
@final_sql = final_sql
|
121
|
+
@arity = arity
|
122
|
+
end
|
123
|
+
|
124
|
+
# Return an array of all objects by running the SQL query for the given arguments.
|
125
|
+
# If a block is given, yields all objects to the block after loading them.
|
126
|
+
def all(*args, &block)
|
127
|
+
@dataset.with_sql_all(sql(*args), &block)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Run the SQL query for the given arguments, yielding each returned row to the block.
|
131
|
+
def each(*args, &block)
|
132
|
+
@dataset.with_sql_each(sql(*args), &block)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Run the SQL query for the given arguments, returning the first row.
|
136
|
+
def first(*args)
|
137
|
+
@dataset.with_sql_first(sql(*args))
|
138
|
+
end
|
139
|
+
|
140
|
+
# Run the SQL query for the given arguments, returning the first value. For this to
|
141
|
+
# make sense, the dataset should return a single row with a single value (or no rows).
|
142
|
+
def get(*args)
|
143
|
+
@dataset.with_sql_single_value(sql(*args))
|
144
|
+
end
|
145
|
+
|
146
|
+
# Return the SQL query to use for the given arguments.
|
147
|
+
def sql(*args)
|
148
|
+
raise Error, "wrong number of arguments (#{args.length} for #{@arity})" unless args.length == @arity
|
149
|
+
s = ''
|
150
|
+
ds = @dataset
|
151
|
+
@fragments.each do |sql, i, transformer|
|
152
|
+
s << sql
|
153
|
+
v = args.fetch(i)
|
154
|
+
v = transformer.call(v) if transformer
|
155
|
+
ds.literal_append(s, v)
|
156
|
+
end
|
157
|
+
if sql = @final_sql
|
158
|
+
s << sql
|
159
|
+
end
|
160
|
+
s
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/lib/sequel/dataset/query.rb
CHANGED
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -62,15 +62,8 @@ module Sequel
|
|
62
62
|
clone(:columns=>columns, :values=>values)._insert_sql
|
63
63
|
end
|
64
64
|
|
65
|
-
#
|
66
|
-
# of an SQL expression.
|
65
|
+
# Append a literal representation of a value to the given SQL string.
|
67
66
|
#
|
68
|
-
# DB[:items].literal("abc'def\\") #=> "'abc''def\\\\'"
|
69
|
-
# DB[:items].literal(:items__id) #=> "items.id"
|
70
|
-
# DB[:items].literal([1, 2, 3]) => "(1, 2, 3)"
|
71
|
-
# DB[:items].literal(DB[:items]) => "(SELECT * FROM items)"
|
72
|
-
# DB[:items].literal(:x + 1 > :y) => "((x + 1) > y)"
|
73
|
-
#
|
74
67
|
# If an unsupported object is given, an +Error+ is raised.
|
75
68
|
def literal_append(sql, v)
|
76
69
|
case v
|
@@ -104,9 +97,9 @@ module Sequel
|
|
104
97
|
when Array
|
105
98
|
literal_array_append(sql, v)
|
106
99
|
when Time
|
107
|
-
|
100
|
+
v.is_a?(SQLTime) ? literal_sqltime_append(sql, v) : literal_time_append(sql, v)
|
108
101
|
when DateTime
|
109
|
-
sql
|
102
|
+
literal_datetime_append(sql, v)
|
110
103
|
when Date
|
111
104
|
sql << literal_date(v)
|
112
105
|
when Dataset
|
@@ -190,6 +183,9 @@ module Sequel
|
|
190
183
|
AS = ' AS '.freeze
|
191
184
|
ASC = ' ASC'.freeze
|
192
185
|
BACKSLASH = "\\".freeze
|
186
|
+
BITCOMP_CLOSE = ") - 1)".freeze
|
187
|
+
BITCOMP_OPEN = "((0 - ".freeze
|
188
|
+
BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR}
|
193
189
|
BOOL_FALSE = "'f'".freeze
|
194
190
|
BOOL_TRUE = "'t'".freeze
|
195
191
|
BRACKET_CLOSE = ']'.freeze
|
@@ -295,13 +291,13 @@ module Sequel
|
|
295
291
|
END
|
296
292
|
end
|
297
293
|
|
298
|
-
#
|
294
|
+
# Append literalization of aliased expression to SQL string.
|
299
295
|
def aliased_expression_sql_append(sql, ae)
|
300
296
|
literal_append(sql, ae.expression)
|
301
297
|
as_sql_append(sql, ae.alias)
|
302
298
|
end
|
303
299
|
|
304
|
-
#
|
300
|
+
# Append literalization of array to SQL string.
|
305
301
|
def array_sql_append(sql, a)
|
306
302
|
if a.empty?
|
307
303
|
sql << ARRAY_EMPTY
|
@@ -312,7 +308,7 @@ module Sequel
|
|
312
308
|
end
|
313
309
|
end
|
314
310
|
|
315
|
-
#
|
311
|
+
# Append literalization of boolean constant to SQL string.
|
316
312
|
def boolean_constant_sql_append(sql, constant)
|
317
313
|
if (constant == true || constant == false) && !supports_where_true?
|
318
314
|
sql << (constant == true ? CONDITION_TRUE : CONDITION_FALSE)
|
@@ -321,7 +317,7 @@ module Sequel
|
|
321
317
|
end
|
322
318
|
end
|
323
319
|
|
324
|
-
#
|
320
|
+
# Append literalization of case expression to SQL string.
|
325
321
|
def case_expression_sql_append(sql, ce)
|
326
322
|
sql << CASE_OPEN
|
327
323
|
if ce.expression?
|
@@ -341,7 +337,7 @@ module Sequel
|
|
341
337
|
sql << CASE_END
|
342
338
|
end
|
343
339
|
|
344
|
-
#
|
340
|
+
# Append literalization of cast expression to SQL string.
|
345
341
|
def cast_sql_append(sql, expr, type)
|
346
342
|
sql << CAST_OPEN
|
347
343
|
literal_append(sql, expr)
|
@@ -349,12 +345,12 @@ module Sequel
|
|
349
345
|
sql << PAREN_CLOSE
|
350
346
|
end
|
351
347
|
|
352
|
-
#
|
348
|
+
# Append literalization of column all selection to SQL string.
|
353
349
|
def column_all_sql_append(sql, ca)
|
354
350
|
qualified_identifier_sql_append(sql, ca.table, WILDCARD)
|
355
351
|
end
|
356
352
|
|
357
|
-
#
|
353
|
+
# Append literalization of complex expression to SQL string.
|
358
354
|
def complex_expression_sql_append(sql, op, args)
|
359
355
|
case op
|
360
356
|
when *IS_OPERATORS
|
@@ -459,18 +455,18 @@ module Sequel
|
|
459
455
|
end
|
460
456
|
end
|
461
457
|
|
462
|
-
#
|
458
|
+
# Append literalization of constant to SQL string.
|
463
459
|
def constant_sql_append(sql, constant)
|
464
460
|
sql << constant.to_s
|
465
461
|
end
|
466
462
|
|
467
|
-
#
|
468
|
-
#
|
463
|
+
# Append literalization of delayed evaluation to SQL string,
|
464
|
+
# causing the delayed evaluation proc to be evaluated.
|
469
465
|
def delayed_evaluation_sql_append(sql, callable)
|
470
466
|
literal_append(sql, callable.call)
|
471
467
|
end
|
472
468
|
|
473
|
-
#
|
469
|
+
# Append literalization of emulated function call to SQL string.
|
474
470
|
# By default, assumes just the function name may need to
|
475
471
|
# be emulated, adapters should set an EMULATED_FUNCTION_MAP
|
476
472
|
# hash mapping emulated functions to native functions in
|
@@ -479,12 +475,12 @@ module Sequel
|
|
479
475
|
_function_sql_append(sql, native_function_name(f.f), f.args)
|
480
476
|
end
|
481
477
|
|
482
|
-
#
|
478
|
+
# Append literalization of function call to SQL string.
|
483
479
|
def function_sql_append(sql, f)
|
484
480
|
_function_sql_append(sql, f.f, f.args)
|
485
481
|
end
|
486
482
|
|
487
|
-
#
|
483
|
+
# Append literalization of JOIN clause without ON or USING to SQL string.
|
488
484
|
def join_clause_sql_append(sql, jc)
|
489
485
|
table = jc.table
|
490
486
|
table_alias = jc.table_alias
|
@@ -494,14 +490,14 @@ module Sequel
|
|
494
490
|
as_sql_append(sql, table_alias) if table_alias
|
495
491
|
end
|
496
492
|
|
497
|
-
#
|
493
|
+
# Append literalization of JOIN ON clause to SQL string.
|
498
494
|
def join_on_clause_sql_append(sql, jc)
|
499
495
|
join_clause_sql_append(sql, jc)
|
500
496
|
sql << ON
|
501
497
|
literal_append(sql, filter_expr(jc.on))
|
502
498
|
end
|
503
499
|
|
504
|
-
#
|
500
|
+
# Append literalization of JOIN USING clause to SQL string.
|
505
501
|
def join_using_clause_sql_append(sql, jc)
|
506
502
|
join_clause_sql_append(sql, jc)
|
507
503
|
sql << USING
|
@@ -509,14 +505,13 @@ module Sequel
|
|
509
505
|
sql << PAREN_CLOSE
|
510
506
|
end
|
511
507
|
|
512
|
-
#
|
508
|
+
# Append literalization of negative boolean constant to SQL string.
|
513
509
|
def negative_boolean_constant_sql_append(sql, constant)
|
514
510
|
sql << NOT_SPACE
|
515
511
|
boolean_constant_sql_append(sql, constant)
|
516
512
|
end
|
517
513
|
|
518
|
-
#
|
519
|
-
# clause.
|
514
|
+
# Append literalization of ordered expression to SQL string.
|
520
515
|
def ordered_expression_sql_append(sql, oe)
|
521
516
|
literal_append(sql, oe.expression)
|
522
517
|
sql << (oe.descending ? DESC : ASC)
|
@@ -528,7 +523,7 @@ module Sequel
|
|
528
523
|
end
|
529
524
|
end
|
530
525
|
|
531
|
-
#
|
526
|
+
# Append literalization of placeholder literal string to SQL string.
|
532
527
|
def placeholder_literal_string_sql_append(sql, pls)
|
533
528
|
args = pls.args
|
534
529
|
str = pls.str
|
@@ -572,8 +567,7 @@ module Sequel
|
|
572
567
|
sql << PAREN_CLOSE if pls.parens
|
573
568
|
end
|
574
569
|
|
575
|
-
#
|
576
|
-
# a table and a column (or schema and table).
|
570
|
+
# Append literalization of qualified identifier to SQL string.
|
577
571
|
# If 3 arguments are given, the 2nd should be the table/qualifier and the third should be
|
578
572
|
# column/qualified. If 2 arguments are given, the 2nd should be an SQL::QualifiedIdentifier.
|
579
573
|
def qualified_identifier_sql_append(sql, table, column=(c = table.column; table = table.table; c))
|
@@ -582,6 +576,7 @@ module Sequel
|
|
582
576
|
identifier_append(sql, column)
|
583
577
|
end
|
584
578
|
|
579
|
+
# Append literalization of unqualified identifier to SQL string.
|
585
580
|
# Adds quoting to identifiers (columns and tables). If identifiers are not
|
586
581
|
# being quoted, returns name as a string. If identifiers are being quoted
|
587
582
|
# quote the name with quoted_identifier.
|
@@ -599,8 +594,7 @@ module Sequel
|
|
599
594
|
end
|
600
595
|
end
|
601
596
|
|
602
|
-
#
|
603
|
-
# quoted (if quoting identifiers)
|
597
|
+
# Append literalization of identifier or unqualified identifier to SQL string.
|
604
598
|
def quote_schema_table_append(sql, table)
|
605
599
|
schema, table = schema_and_table(table)
|
606
600
|
if schema
|
@@ -610,6 +604,7 @@ module Sequel
|
|
610
604
|
quote_identifier_append(sql, table)
|
611
605
|
end
|
612
606
|
|
607
|
+
# Append literalization of quoted identifier to SQL string.
|
613
608
|
# This method quotes the given name with the SQL standard double quote.
|
614
609
|
# should be overridden by subclasses to provide quoting not matching the
|
615
610
|
# SQL standard, such as backtick (used by MySQL and SQLite).
|
@@ -657,7 +652,7 @@ module Sequel
|
|
657
652
|
end
|
658
653
|
end
|
659
654
|
|
660
|
-
#
|
655
|
+
# Append literalization of subscripts (SQL array accesses) to SQL string.
|
661
656
|
def subscript_sql_append(sql, s)
|
662
657
|
literal_append(sql, s.f)
|
663
658
|
sql << BRACKET_OPEN
|
@@ -673,7 +668,7 @@ module Sequel
|
|
673
668
|
sql << BRACKET_CLOSE
|
674
669
|
end
|
675
670
|
|
676
|
-
#
|
671
|
+
# Append literalization of windows (for window functions) to SQL string.
|
677
672
|
def window_sql_append(sql, opts)
|
678
673
|
raise(Error, 'This dataset does not support window functions') unless supports_window_functions?
|
679
674
|
sql << PAREN_OPEN
|
@@ -714,7 +709,7 @@ module Sequel
|
|
714
709
|
sql << PAREN_CLOSE
|
715
710
|
end
|
716
711
|
|
717
|
-
#
|
712
|
+
# Append literalization of window function calls to SQL string.
|
718
713
|
def window_function_sql_append(sql, function, window)
|
719
714
|
literal_append(sql, function)
|
720
715
|
sql << OVER
|
@@ -815,7 +810,7 @@ module Sequel
|
|
815
810
|
options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
|
816
811
|
end
|
817
812
|
|
818
|
-
#
|
813
|
+
# Append aliasing expression to SQL string.
|
819
814
|
def as_sql_append(sql, aliaz)
|
820
815
|
sql << AS
|
821
816
|
quote_identifier_append(sql, aliaz)
|
@@ -840,6 +835,7 @@ module Sequel
|
|
840
835
|
sql
|
841
836
|
end
|
842
837
|
|
838
|
+
# Append column list to SQL string.
|
843
839
|
# Converts an array of column names into a comma seperated string of
|
844
840
|
# column names. If the array is empty, a wildcard (*) is returned.
|
845
841
|
def column_list_append(sql, columns)
|
@@ -850,24 +846,50 @@ module Sequel
|
|
850
846
|
end
|
851
847
|
end
|
852
848
|
|
853
|
-
# Yield each
|
854
|
-
# return
|
855
|
-
# two arguments.
|
856
|
-
#
|
857
|
-
# as the first argument, representing the application of the block to
|
858
|
-
# the previous arguments.
|
849
|
+
# Yield each pair of arguments to the block, which should
|
850
|
+
# return an object representing the SQL expression for those
|
851
|
+
# two arguments. For more than two arguments, the first
|
852
|
+
# argument to the block will be result of the previous block call.
|
859
853
|
def complex_expression_arg_pairs(args)
|
860
854
|
case args.length
|
861
855
|
when 1
|
862
|
-
|
856
|
+
args.at(0)
|
863
857
|
when 2
|
864
858
|
yield args.at(0), args.at(1)
|
865
859
|
else
|
866
|
-
args.inject{|m, a|
|
860
|
+
args.inject{|m, a| yield(m, a)}
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
# Append the literalization of the args using complex_expression_arg_pairs
|
865
|
+
# to the given SQL string, used when database operator/function is 2-ary
|
866
|
+
# where Sequel expression is N-ary.
|
867
|
+
def complex_expression_arg_pairs_append(sql, args, &block)
|
868
|
+
literal_append(sql, complex_expression_arg_pairs(args, &block))
|
869
|
+
end
|
870
|
+
|
871
|
+
# Append literalization of complex expression to SQL string, for
|
872
|
+
# operators unsupported by some databases. Used by adapters for databases
|
873
|
+
# that don't support the operators natively.
|
874
|
+
def complex_expression_emulate_append(sql, op, args)
|
875
|
+
case op
|
876
|
+
when :%
|
877
|
+
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:MOD, a, b)}
|
878
|
+
when :>>
|
879
|
+
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel./(a, Sequel.function(:power, 2, b))}
|
880
|
+
when :<<
|
881
|
+
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.*(a, Sequel.function(:power, 2, b))}
|
882
|
+
when :&, :|, :^
|
883
|
+
f = BITWISE_METHOD_MAP[op]
|
884
|
+
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(f, a, b)}
|
885
|
+
when :'B~'
|
886
|
+
sql << BITCOMP_OPEN
|
887
|
+
literal_append(sql, args.at(0))
|
888
|
+
sql << BITCOMP_CLOSE
|
867
889
|
end
|
868
890
|
end
|
869
891
|
|
870
|
-
#
|
892
|
+
# Append literalization of dataset used in UNION/INTERSECT/EXCEPT clause to SQL string.
|
871
893
|
def compound_dataset_sql_append(sql, ds)
|
872
894
|
subselect_sql_append(sql, ds)
|
873
895
|
end
|
@@ -891,8 +913,7 @@ module Sequel
|
|
891
913
|
sql << DELETE
|
892
914
|
end
|
893
915
|
|
894
|
-
#
|
895
|
-
# expressions.
|
916
|
+
# Append literalization of array of expressions to SQL string.
|
896
917
|
def expression_list_append(sql, columns)
|
897
918
|
c = false
|
898
919
|
co = COMMA
|
@@ -941,8 +962,8 @@ module Sequel
|
|
941
962
|
sprintf(FORMAT_TIMESTAMP_USEC, usec)
|
942
963
|
end
|
943
964
|
|
944
|
-
# Append
|
945
|
-
#
|
965
|
+
# Append literalization of identifier to SQL string, considering regular strings
|
966
|
+
# as SQL identifiers instead of SQL strings.
|
946
967
|
def identifier_append(sql, v)
|
947
968
|
if v.is_a?(String)
|
948
969
|
case v
|
@@ -958,7 +979,7 @@ module Sequel
|
|
958
979
|
end
|
959
980
|
end
|
960
981
|
|
961
|
-
# Append
|
982
|
+
# Append literalization of array of identifiers to SQL string.
|
962
983
|
def identifier_list_append(sql, args)
|
963
984
|
c = false
|
964
985
|
comma = COMMA
|
@@ -975,7 +996,6 @@ module Sequel
|
|
975
996
|
(i = identifier_input_method) ? v.to_s.send(i) : v.to_s
|
976
997
|
end
|
977
998
|
|
978
|
-
# SQL fragment specifying the table to insert INTO
|
979
999
|
def insert_into_sql(sql)
|
980
1000
|
sql << INTO
|
981
1001
|
source_list_append(sql, @opts[:from])
|
@@ -986,7 +1006,6 @@ module Sequel
|
|
986
1006
|
INSERT_CLAUSE_METHODS
|
987
1007
|
end
|
988
1008
|
|
989
|
-
# SQL fragment specifying the columns to insert into
|
990
1009
|
def insert_columns_sql(sql)
|
991
1010
|
columns = opts[:columns]
|
992
1011
|
if columns && !columns.empty?
|
@@ -1000,7 +1019,6 @@ module Sequel
|
|
1000
1019
|
sql << INSERT
|
1001
1020
|
end
|
1002
1021
|
|
1003
|
-
# SQL fragment specifying the values to insert.
|
1004
1022
|
def insert_values_sql(sql)
|
1005
1023
|
case values = opts[:values]
|
1006
1024
|
when Array
|
@@ -1020,7 +1038,6 @@ module Sequel
|
|
1020
1038
|
end
|
1021
1039
|
end
|
1022
1040
|
|
1023
|
-
# SQL fragment specifying the values to return.
|
1024
1041
|
def insert_returning_sql(sql)
|
1025
1042
|
if opts.has_key?(:returning)
|
1026
1043
|
sql << RETURNING
|
@@ -1041,7 +1058,8 @@ module Sequel
|
|
1041
1058
|
(opts[:from].is_a?(Array) && opts[:from].size > 1) || opts[:join]
|
1042
1059
|
end
|
1043
1060
|
|
1044
|
-
#
|
1061
|
+
# Append a literalization of the array to SQL string.
|
1062
|
+
# Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
|
1045
1063
|
def literal_array_append(sql, v)
|
1046
1064
|
if Sequel.condition_specifier?(v)
|
1047
1065
|
literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
|
@@ -1056,12 +1074,12 @@ module Sequel
|
|
1056
1074
|
v.nan? || v.infinite? ? "'#{d}'" : d
|
1057
1075
|
end
|
1058
1076
|
|
1059
|
-
#
|
1077
|
+
# Append literalization of SQL::Blob to SQL string.
|
1060
1078
|
def literal_blob_append(sql, v)
|
1061
1079
|
literal_string_append(sql, v)
|
1062
1080
|
end
|
1063
1081
|
|
1064
|
-
#
|
1082
|
+
# Append literalization of dataset to SQL string. Does a subselect inside parantheses.
|
1065
1083
|
def literal_dataset_append(sql, v)
|
1066
1084
|
sql << LATERAL if v.opts[:lateral]
|
1067
1085
|
sql << PAREN_OPEN
|
@@ -1083,7 +1101,12 @@ module Sequel
|
|
1083
1101
|
format_timestamp(v)
|
1084
1102
|
end
|
1085
1103
|
|
1086
|
-
#
|
1104
|
+
# Append literalization of DateTime to SQL string.
|
1105
|
+
def literal_datetime_append(sql, v)
|
1106
|
+
sql << literal_datetime(v)
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
# Append literalization of SQL::Expression to SQL string.
|
1087
1110
|
def literal_expression_append(sql, v)
|
1088
1111
|
v.to_s_append(self, sql)
|
1089
1112
|
end
|
@@ -1098,7 +1121,7 @@ module Sequel
|
|
1098
1121
|
v.to_s
|
1099
1122
|
end
|
1100
1123
|
|
1101
|
-
#
|
1124
|
+
# Append literalization of Hash to SQL string, treating hash as a boolean expression.
|
1102
1125
|
def literal_hash_append(sql, v)
|
1103
1126
|
literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
|
1104
1127
|
end
|
@@ -1113,11 +1136,9 @@ module Sequel
|
|
1113
1136
|
NULL
|
1114
1137
|
end
|
1115
1138
|
|
1116
|
-
#
|
1117
|
-
# Calls +
|
1118
|
-
#
|
1119
|
-
# provided and should add that method to Sequel::Dataset, allowing for adapters
|
1120
|
-
# to provide customized literalizations.
|
1139
|
+
# Append a literalization of the object to the given SQL string.
|
1140
|
+
# Calls +sql_literal_append+ if object responds to it, otherwise
|
1141
|
+
# calls +sql_literal+ if object responds to it, otherwise raises an error.
|
1121
1142
|
# If a database specific type is allowed, this should be overriden in a subclass.
|
1122
1143
|
def literal_other_append(sql, v)
|
1123
1144
|
if v.respond_to?(:sql_literal_append)
|
@@ -1134,19 +1155,17 @@ module Sequel
|
|
1134
1155
|
v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
|
1135
1156
|
end
|
1136
1157
|
|
1137
|
-
#
|
1158
|
+
# Append literalization of Sequel::SQLTime to SQL string.
|
1159
|
+
def literal_sqltime_append(sql, v)
|
1160
|
+
sql << literal_sqltime(v)
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
# Append literalization of string to SQL string.
|
1138
1164
|
def literal_string_append(sql, v)
|
1139
1165
|
sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
|
1140
1166
|
end
|
1141
1167
|
|
1142
|
-
#
|
1143
|
-
# notation in order to express qualified (two underscores) and aliased
|
1144
|
-
# (three underscores) columns:
|
1145
|
-
#
|
1146
|
-
# dataset.literal(:abc) #=> "abc"
|
1147
|
-
# dataset.literal(:abc___a) #=> "abc AS a"
|
1148
|
-
# dataset.literal(:items__abc) #=> "items.abc"
|
1149
|
-
# dataset.literal(:items__abc___a) #=> "items.abc AS a"
|
1168
|
+
# Append literalization of symbol to SQL string.
|
1150
1169
|
def literal_symbol_append(sql, v)
|
1151
1170
|
c_table, column, c_alias = split_symbol(v)
|
1152
1171
|
if c_table
|
@@ -1162,6 +1181,11 @@ module Sequel
|
|
1162
1181
|
format_timestamp(v)
|
1163
1182
|
end
|
1164
1183
|
|
1184
|
+
# Append literalization of Time to SQL string.
|
1185
|
+
def literal_time_append(sql, v)
|
1186
|
+
sql << literal_time(v)
|
1187
|
+
end
|
1188
|
+
|
1165
1189
|
# SQL fragment for true
|
1166
1190
|
def literal_true
|
1167
1191
|
BOOL_TRUE
|
@@ -1203,13 +1227,11 @@ module Sequel
|
|
1203
1227
|
SELECT_CLAUSE_METHODS
|
1204
1228
|
end
|
1205
1229
|
|
1206
|
-
# Modify the sql to add the columns selected
|
1207
1230
|
def select_columns_sql(sql)
|
1208
1231
|
sql << SPACE
|
1209
1232
|
column_list_append(sql, @opts[:select])
|
1210
1233
|
end
|
1211
1234
|
|
1212
|
-
# Modify the sql to add the DISTINCT modifier
|
1213
1235
|
def select_distinct_sql(sql)
|
1214
1236
|
if distinct = @opts[:distinct]
|
1215
1237
|
sql << DISTINCT
|
@@ -1234,7 +1256,6 @@ module Sequel
|
|
1234
1256
|
end
|
1235
1257
|
end
|
1236
1258
|
|
1237
|
-
# Modify the sql to add the list of tables to select FROM
|
1238
1259
|
def select_from_sql(sql)
|
1239
1260
|
if f = @opts[:from]
|
1240
1261
|
sql << FROM
|
@@ -1243,7 +1264,6 @@ module Sequel
|
|
1243
1264
|
end
|
1244
1265
|
alias delete_from_sql select_from_sql
|
1245
1266
|
|
1246
|
-
# Modify the sql to add the expressions to GROUP BY
|
1247
1267
|
def select_group_sql(sql)
|
1248
1268
|
if group = @opts[:group]
|
1249
1269
|
sql << GROUP_BY
|
@@ -1262,7 +1282,6 @@ module Sequel
|
|
1262
1282
|
end
|
1263
1283
|
end
|
1264
1284
|
|
1265
|
-
# Modify the sql to add the filter criteria in the HAVING clause
|
1266
1285
|
def select_having_sql(sql)
|
1267
1286
|
if having = @opts[:having]
|
1268
1287
|
sql << HAVING
|
@@ -1270,14 +1289,12 @@ module Sequel
|
|
1270
1289
|
end
|
1271
1290
|
end
|
1272
1291
|
|
1273
|
-
# Modify the sql to add the list of tables to JOIN to
|
1274
1292
|
def select_join_sql(sql)
|
1275
1293
|
if js = @opts[:join]
|
1276
1294
|
js.each{|j| literal_append(sql, j)}
|
1277
1295
|
end
|
1278
1296
|
end
|
1279
1297
|
|
1280
|
-
# Modify the sql to limit the number of rows returned and offset
|
1281
1298
|
def select_limit_sql(sql)
|
1282
1299
|
if l = @opts[:limit]
|
1283
1300
|
sql << LIMIT
|
@@ -1289,7 +1306,6 @@ module Sequel
|
|
1289
1306
|
end
|
1290
1307
|
end
|
1291
1308
|
|
1292
|
-
# Modify the sql to support the different types of locking modes.
|
1293
1309
|
def select_lock_sql(sql)
|
1294
1310
|
case l = @opts[:lock]
|
1295
1311
|
when :update
|
@@ -1299,7 +1315,6 @@ module Sequel
|
|
1299
1315
|
end
|
1300
1316
|
end
|
1301
1317
|
|
1302
|
-
# Modify the sql to add the expressions to ORDER BY
|
1303
1318
|
def select_order_sql(sql)
|
1304
1319
|
if o = @opts[:order]
|
1305
1320
|
sql << ORDER_BY
|
@@ -1313,7 +1328,6 @@ module Sequel
|
|
1313
1328
|
sql << SELECT
|
1314
1329
|
end
|
1315
1330
|
|
1316
|
-
# Modify the sql to add the filter criteria in the WHERE clause
|
1317
1331
|
def select_where_sql(sql)
|
1318
1332
|
if w = @opts[:where]
|
1319
1333
|
sql << WHERE
|
@@ -1323,7 +1337,6 @@ module Sequel
|
|
1323
1337
|
alias delete_where_sql select_where_sql
|
1324
1338
|
alias update_where_sql select_where_sql
|
1325
1339
|
|
1326
|
-
# SQL Fragment specifying the WITH clause
|
1327
1340
|
def select_with_sql(sql)
|
1328
1341
|
ws = opts[:with]
|
1329
1342
|
return if !ws || ws.empty?
|
@@ -1353,7 +1366,8 @@ module Sequel
|
|
1353
1366
|
SQL_WITH
|
1354
1367
|
end
|
1355
1368
|
|
1356
|
-
#
|
1369
|
+
# Append literalization of array of sources/tables to SQL string, raising an Error if there
|
1370
|
+
# are no sources.
|
1357
1371
|
def source_list_append(sql, sources)
|
1358
1372
|
raise(Error, 'No source specified for query') if sources.nil? || sources == []
|
1359
1373
|
identifier_list_append(sql, sources)
|
@@ -1372,7 +1386,8 @@ module Sequel
|
|
1372
1386
|
|
1373
1387
|
# SQL to use if this dataset uses static SQL. Since static SQL
|
1374
1388
|
# can be a PlaceholderLiteralString in addition to a String,
|
1375
|
-
# we literalize nonstrings.
|
1389
|
+
# we literalize nonstrings. If there is an append_sql for this
|
1390
|
+
# dataset, append to that SQL instead of returning the value.
|
1376
1391
|
def static_sql(sql)
|
1377
1392
|
if append_sql = @opts[:append_sql]
|
1378
1393
|
if sql.is_a?(String)
|
@@ -1389,7 +1404,7 @@ module Sequel
|
|
1389
1404
|
end
|
1390
1405
|
end
|
1391
1406
|
|
1392
|
-
#
|
1407
|
+
# Append literalization of the subselect to SQL String.
|
1393
1408
|
def subselect_sql_append(sql, ds)
|
1394
1409
|
ds.clone(:append_sql=>sql).sql
|
1395
1410
|
end
|
@@ -1399,15 +1414,12 @@ module Sequel
|
|
1399
1414
|
UPDATE_CLAUSE_METHODS
|
1400
1415
|
end
|
1401
1416
|
|
1402
|
-
# SQL fragment specifying the tables from with to delete.
|
1403
|
-
# Includes join table if modifying joins is allowed.
|
1404
1417
|
def update_table_sql(sql)
|
1405
1418
|
sql << SPACE
|
1406
1419
|
source_list_append(sql, @opts[:from])
|
1407
1420
|
select_join_sql(sql) if supports_modifying_joins?
|
1408
1421
|
end
|
1409
1422
|
|
1410
|
-
# The SQL fragment specifying the columns and values to SET.
|
1411
1423
|
def update_set_sql(sql)
|
1412
1424
|
values = opts[:values]
|
1413
1425
|
sql << SET
|