sequel 3.29.0 → 3.30.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 +35 -3
- data/Rakefile +2 -1
- data/doc/association_basics.rdoc +11 -0
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/testing.rdoc +17 -3
- data/lib/sequel/adapters/amalgalite.rb +2 -2
- data/lib/sequel/adapters/do/mysql.rb +5 -2
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +126 -43
- data/lib/sequel/adapters/jdbc/as400.rb +11 -3
- data/lib/sequel/adapters/jdbc/db2.rb +2 -1
- data/lib/sequel/adapters/jdbc/derby.rb +44 -19
- data/lib/sequel/adapters/jdbc/h2.rb +32 -19
- data/lib/sequel/adapters/jdbc/hsqldb.rb +21 -17
- data/lib/sequel/adapters/jdbc/jtds.rb +9 -4
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +21 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +48 -18
- data/lib/sequel/adapters/mock.rb +2 -1
- data/lib/sequel/adapters/mysql.rb +4 -2
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +6 -6
- data/lib/sequel/adapters/postgres.rb +25 -12
- data/lib/sequel/adapters/shared/access.rb +14 -6
- data/lib/sequel/adapters/shared/db2.rb +36 -13
- data/lib/sequel/adapters/shared/firebird.rb +12 -5
- data/lib/sequel/adapters/shared/informix.rb +11 -3
- data/lib/sequel/adapters/shared/mssql.rb +94 -47
- data/lib/sequel/adapters/shared/mysql.rb +107 -49
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +54 -27
- data/lib/sequel/adapters/shared/postgres.rb +65 -26
- data/lib/sequel/adapters/shared/progress.rb +4 -1
- data/lib/sequel/adapters/shared/sqlite.rb +36 -20
- data/lib/sequel/adapters/sqlite.rb +2 -3
- data/lib/sequel/adapters/swift/mysql.rb +3 -2
- data/lib/sequel/adapters/swift/sqlite.rb +2 -2
- data/lib/sequel/adapters/tinytds.rb +14 -8
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -4
- data/lib/sequel/database/misc.rb +6 -2
- data/lib/sequel/dataset/graph.rb +33 -7
- data/lib/sequel/dataset/prepared_statements.rb +19 -5
- data/lib/sequel/dataset/sql.rb +611 -201
- data/lib/sequel/model/associations.rb +12 -5
- data/lib/sequel/model/base.rb +20 -5
- data/lib/sequel/plugins/sharding.rb +9 -29
- data/lib/sequel/sql.rb +2 -1
- data/lib/sequel/timezones.rb +14 -4
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +10 -0
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/core/core_sql_spec.rb +3 -1
- data/spec/core/database_spec.rb +42 -0
- data/spec/core/dataset_spec.rb +10 -3
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/object_graph_spec.rb +38 -0
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +35 -39
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +30 -82
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +33 -103
- data/spec/extensions/instance_filters_spec.rb +10 -21
- data/spec/extensions/instance_hooks_spec.rb +6 -24
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +135 -277
- data/spec/extensions/migration_spec.rb +18 -15
- data/spec/extensions/named_timezones_spec.rb +1 -1
- data/spec/extensions/nested_attributes_spec.rb +97 -92
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +22 -37
- data/spec/extensions/prepared_statements_safe_spec.rb +9 -27
- data/spec/extensions/prepared_statements_spec.rb +11 -30
- data/spec/extensions/prepared_statements_with_pk_spec.rb +6 -13
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +3 -6
- data/spec/extensions/serialization_spec.rb +20 -32
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +14 -36
- data/spec/extensions/spec_helper.rb +10 -64
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +1 -2
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/integration/dataset_test.rb +16 -4
- data/spec/integration/prepared_statement_test.rb +4 -2
- data/spec/model/eager_loading_spec.rb +16 -0
- data/spec/model/model_spec.rb +15 -1
- data/spec/model/record_spec.rb +60 -0
- metadata +23 -40
|
@@ -31,7 +31,10 @@ module Sequel
|
|
|
31
31
|
# The Progress adapter targets Progress 9, so it silently ignores the option.
|
|
32
32
|
def select_limit_sql(sql)
|
|
33
33
|
raise(Error, "OFFSET not supported") if @opts[:offset]
|
|
34
|
-
#
|
|
34
|
+
# if l = @opts[:limit]
|
|
35
|
+
# sql << " TOP "
|
|
36
|
+
# literal_append(sql, l)
|
|
37
|
+
# end
|
|
35
38
|
end
|
|
36
39
|
end
|
|
37
40
|
end
|
|
@@ -396,24 +396,36 @@ module Sequel
|
|
|
396
396
|
|
|
397
397
|
# Instance methods for datasets that connect to an SQLite database
|
|
398
398
|
module DatasetMethods
|
|
399
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
|
|
400
|
-
COMMA_SEPARATOR = ', '.freeze
|
|
399
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
|
|
401
400
|
CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
|
|
402
401
|
EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}
|
|
402
|
+
NOT_SPACE = Dataset::NOT_SPACE
|
|
403
|
+
COMMA = Dataset::COMMA
|
|
404
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
405
|
+
AS = Dataset::AS
|
|
406
|
+
APOS = Dataset::APOS
|
|
407
|
+
EXTRACT_OPEN = "CAST(strftime(".freeze
|
|
408
|
+
EXRACT_CLOSE = ') AS '.freeze
|
|
409
|
+
NUMERIC = 'NUMERIC'.freeze
|
|
410
|
+
INTEGER = 'INTEGER'.freeze
|
|
411
|
+
BACKTICK = '`'.freeze
|
|
412
|
+
BLOB_START = "X'".freeze
|
|
413
|
+
HSTAR = "H*".freeze
|
|
403
414
|
|
|
404
415
|
# SQLite does not support pattern matching via regular expressions.
|
|
405
416
|
# SQLite is case insensitive (depending on pragma), so use LIKE for
|
|
406
417
|
# ILIKE.
|
|
407
|
-
def
|
|
418
|
+
def complex_expression_sql_append(sql, op, args)
|
|
408
419
|
case op
|
|
409
420
|
when :~, :'!~', :'~*', :'!~*'
|
|
410
421
|
raise Error, "SQLite does not support pattern matching via regular expressions"
|
|
411
422
|
when :ILIKE
|
|
412
|
-
super(:LIKE, args.map{|a| SQL::Function.new(:upper, a)})
|
|
423
|
+
super(sql, :LIKE, args.map{|a| SQL::Function.new(:upper, a)})
|
|
413
424
|
when :"NOT LIKE", :"NOT ILIKE"
|
|
414
|
-
|
|
425
|
+
sql << NOT_SPACE
|
|
426
|
+
complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
|
|
415
427
|
when :^
|
|
416
|
-
complex_expression_arg_pairs(args) do |a, b|
|
|
428
|
+
sql << complex_expression_arg_pairs(args) do |a, b|
|
|
417
429
|
a = literal(a)
|
|
418
430
|
b = literal(b)
|
|
419
431
|
"((~(#{a} & #{b})) & (#{a} | #{b}))"
|
|
@@ -421,16 +433,21 @@ module Sequel
|
|
|
421
433
|
when :extract
|
|
422
434
|
part = args.at(0)
|
|
423
435
|
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
|
|
424
|
-
|
|
425
|
-
|
|
436
|
+
sql << EXTRACT_OPEN << format << COMMA
|
|
437
|
+
literal_append(sql, args.at(1))
|
|
438
|
+
sql << EXRACT_CLOSE << (part == :second ? NUMERIC : INTEGER) << PAREN_CLOSE
|
|
426
439
|
else
|
|
427
|
-
super
|
|
440
|
+
super
|
|
428
441
|
end
|
|
429
442
|
end
|
|
430
443
|
|
|
431
444
|
# MSSQL doesn't support the SQL standard CURRENT_DATE or CURRENT_TIME
|
|
432
|
-
def
|
|
433
|
-
CONSTANT_MAP[constant]
|
|
445
|
+
def constant_sql_append(sql, constant)
|
|
446
|
+
if c = CONSTANT_MAP[constant]
|
|
447
|
+
sql << c
|
|
448
|
+
else
|
|
449
|
+
super
|
|
450
|
+
end
|
|
434
451
|
end
|
|
435
452
|
|
|
436
453
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
|
@@ -454,8 +471,8 @@ module Sequel
|
|
|
454
471
|
end
|
|
455
472
|
|
|
456
473
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
|
457
|
-
def
|
|
458
|
-
|
|
474
|
+
def quoted_identifier_append(sql, c)
|
|
475
|
+
sql << BACKTICK << c.to_s << BACKTICK
|
|
459
476
|
end
|
|
460
477
|
|
|
461
478
|
# When a qualified column is selected on SQLite and the qualifier
|
|
@@ -500,9 +517,10 @@ module Sequel
|
|
|
500
517
|
private
|
|
501
518
|
|
|
502
519
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
|
503
|
-
def
|
|
520
|
+
def as_sql_append(sql, aliaz)
|
|
504
521
|
aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
|
|
505
|
-
|
|
522
|
+
sql << AS
|
|
523
|
+
literal_append(sql, aliaz.to_s)
|
|
506
524
|
end
|
|
507
525
|
|
|
508
526
|
# If col is a qualified column, alias it to the same as the column name
|
|
@@ -524,14 +542,12 @@ module Sequel
|
|
|
524
542
|
|
|
525
543
|
# SQL fragment specifying a list of identifiers
|
|
526
544
|
def identifier_list(columns)
|
|
527
|
-
columns.map{|i| quote_identifier(i)}.join(
|
|
545
|
+
columns.map{|i| quote_identifier(i)}.join(COMMA)
|
|
528
546
|
end
|
|
529
547
|
|
|
530
548
|
# SQLite uses a preceding X for hex escaping strings
|
|
531
|
-
def
|
|
532
|
-
|
|
533
|
-
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
534
|
-
"X'#{blob}'"
|
|
549
|
+
def literal_blob_append(sql, v)
|
|
550
|
+
sql << BLOB_START << v.unpack(HSTAR).first << APOS
|
|
535
551
|
end
|
|
536
552
|
|
|
537
553
|
# SQLite does not support the SQL WITH clause
|
|
@@ -323,7 +323,6 @@ module Sequel
|
|
|
323
323
|
ps.prepared_statement_name = name
|
|
324
324
|
db.prepared_statements[name] = ps
|
|
325
325
|
end
|
|
326
|
-
ps.prepared_sql
|
|
327
326
|
ps
|
|
328
327
|
end
|
|
329
328
|
|
|
@@ -335,8 +334,8 @@ module Sequel
|
|
|
335
334
|
end
|
|
336
335
|
|
|
337
336
|
# Quote the string using the adapter class method.
|
|
338
|
-
def
|
|
339
|
-
"'
|
|
337
|
+
def literal_string_append(sql, v)
|
|
338
|
+
sql << "'" << ::SQLite3::Database.quote(v) << "'"
|
|
340
339
|
end
|
|
341
340
|
|
|
342
341
|
# SQLite uses a : before the name of the argument as a placeholder.
|
|
@@ -34,6 +34,7 @@ module Sequel
|
|
|
34
34
|
# Dataset class for MySQL datasets accessed via Swift.
|
|
35
35
|
class Dataset < Swift::Dataset
|
|
36
36
|
include Sequel::MySQL::DatasetMethods
|
|
37
|
+
APOS = Dataset::APOS
|
|
37
38
|
|
|
38
39
|
# Use execute_insert to execute the replace_sql.
|
|
39
40
|
def replace(*args)
|
|
@@ -43,8 +44,8 @@ module Sequel
|
|
|
43
44
|
private
|
|
44
45
|
|
|
45
46
|
# Use Swift's escape method for quoting.
|
|
46
|
-
def
|
|
47
|
-
db.synchronize{|c|
|
|
47
|
+
def literal_string_append(sql, s)
|
|
48
|
+
sql << APOS << db.synchronize{|c| c.escape(s)} << APOS
|
|
48
49
|
end
|
|
49
50
|
end
|
|
50
51
|
end
|
|
@@ -186,17 +186,17 @@ module Sequel
|
|
|
186
186
|
# Run execute_select on the database with the given SQL and the stored
|
|
187
187
|
# bind arguments.
|
|
188
188
|
def execute(sql, opts={}, &block)
|
|
189
|
-
super(
|
|
189
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
# Same as execute, explicit due to intricacies of alias and super.
|
|
193
193
|
def execute_dui(sql, opts={}, &block)
|
|
194
|
-
super(
|
|
194
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
# Same as execute, explicit due to intricacies of alias and super.
|
|
198
198
|
def execute_insert(sql, opts={}, &block)
|
|
199
|
-
super(
|
|
199
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
200
200
|
end
|
|
201
201
|
end
|
|
202
202
|
|
|
@@ -206,15 +206,15 @@ module Sequel
|
|
|
206
206
|
execute(sql) do |result|
|
|
207
207
|
each_opts = {:cache_rows=>false}
|
|
208
208
|
each_opts[:timezone] = :utc if db.timezone == :utc
|
|
209
|
-
rn = row_number_column if @opts[:offset]
|
|
209
|
+
rn = row_number_column if offset = @opts[:offset]
|
|
210
210
|
columns = cols = result.fields.map{|c| output_identifier(c)}
|
|
211
|
-
if
|
|
211
|
+
if offset
|
|
212
212
|
rn = row_number_column
|
|
213
213
|
columns = columns.dup
|
|
214
214
|
columns.delete(rn)
|
|
215
215
|
end
|
|
216
216
|
@columns = columns
|
|
217
|
-
if identifier_output_method
|
|
217
|
+
#if identifier_output_method
|
|
218
218
|
each_opts[:as] = :array
|
|
219
219
|
result.each(each_opts) do |r|
|
|
220
220
|
h = {}
|
|
@@ -222,6 +222,10 @@ module Sequel
|
|
|
222
222
|
h.delete(rn) if rn
|
|
223
223
|
yield h
|
|
224
224
|
end
|
|
225
|
+
=begin
|
|
226
|
+
# Temporarily disable this optimization, as tiny_tds uses string keys
|
|
227
|
+
# if result.fields is called before result.each(:symbolize_keys=>true).
|
|
228
|
+
# See https://github.com/rails-sqlserver/tiny_tds/issues/57
|
|
225
229
|
else
|
|
226
230
|
each_opts[:symbolize_keys] = true
|
|
227
231
|
if offset
|
|
@@ -233,6 +237,7 @@ module Sequel
|
|
|
233
237
|
result.each(each_opts, &Proc.new)
|
|
234
238
|
end
|
|
235
239
|
end
|
|
240
|
+
=end
|
|
236
241
|
end
|
|
237
242
|
self
|
|
238
243
|
end
|
|
@@ -252,8 +257,9 @@ module Sequel
|
|
|
252
257
|
private
|
|
253
258
|
|
|
254
259
|
# Properly escape the given string +v+.
|
|
255
|
-
def
|
|
256
|
-
|
|
260
|
+
def literal_string_append(sql, v)
|
|
261
|
+
sql << 'N' if mssql_unicode_strings
|
|
262
|
+
sql << "'" << db.synchronize{|c| c.escape(v)} << "'"
|
|
257
263
|
end
|
|
258
264
|
end
|
|
259
265
|
end
|
|
@@ -3,7 +3,7 @@ module Sequel
|
|
|
3
3
|
# When a subselect that uses :offset is used in IN or NOT IN,
|
|
4
4
|
# use a nested subselect that only includes the first column
|
|
5
5
|
# instead of the ROW_NUMBER column added by the emulated offset support.
|
|
6
|
-
def
|
|
6
|
+
def complex_expression_sql_append(sql, op, args)
|
|
7
7
|
case op
|
|
8
8
|
when :IN, :"NOT IN"
|
|
9
9
|
ds = args.at(1)
|
|
@@ -22,7 +22,7 @@ module Sequel
|
|
|
22
22
|
when SQL::QualifiedIdentifier
|
|
23
23
|
c = SQL::Identifier.new(c.column)
|
|
24
24
|
end
|
|
25
|
-
super(op, [args.at(0), ds.from_self.select(c)])
|
|
25
|
+
super(sql, op, [args.at(0), ds.from_self.select(c)])
|
|
26
26
|
else
|
|
27
27
|
super
|
|
28
28
|
end
|
|
@@ -44,12 +44,15 @@ module Sequel
|
|
|
44
44
|
raise(Error, "#{db.database_type} requires an order be provided if using an offset") unless order = @opts[:order]
|
|
45
45
|
dsa1 = dataset_alias(1)
|
|
46
46
|
rn = row_number_column
|
|
47
|
-
|
|
47
|
+
sql = @opts[:append_sql] || ''
|
|
48
|
+
subselect_sql_append(sql, unlimited.
|
|
48
49
|
unordered.
|
|
49
50
|
select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
|
|
50
51
|
from_self(:alias=>dsa1).
|
|
51
52
|
limit(@opts[:limit]).
|
|
52
|
-
where(SQL::Identifier.new(rn) > o)
|
|
53
|
+
where(SQL::Identifier.new(rn) > o).
|
|
54
|
+
order(rn))
|
|
55
|
+
sql
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
private
|
data/lib/sequel/database/misc.rb
CHANGED
|
@@ -69,9 +69,11 @@ module Sequel
|
|
|
69
69
|
# If a transaction is not currently in process, yield to the block immediately.
|
|
70
70
|
# Otherwise, add the block to the list of blocks to call after the currently
|
|
71
71
|
# in progress transaction commits (and only if it commits).
|
|
72
|
+
# Options:
|
|
73
|
+
# :server :: The server/shard to use.
|
|
72
74
|
def after_commit(opts={}, &block)
|
|
73
75
|
raise Error, "must provide block to after_commit" unless block
|
|
74
|
-
synchronize(opts) do |conn|
|
|
76
|
+
synchronize(opts[:server]) do |conn|
|
|
75
77
|
if h = @transactions[conn]
|
|
76
78
|
raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
|
|
77
79
|
(h[:after_commit] ||= []) << block
|
|
@@ -84,9 +86,11 @@ module Sequel
|
|
|
84
86
|
# If a transaction is not currently in progress, ignore the block.
|
|
85
87
|
# Otherwise, add the block to the list of the blocks to call after the currently
|
|
86
88
|
# in progress transaction rolls back (and only if it rolls back).
|
|
89
|
+
# Options:
|
|
90
|
+
# :server :: The server/shard to use.
|
|
87
91
|
def after_rollback(opts={}, &block)
|
|
88
92
|
raise Error, "must provide block to after_rollback" unless block
|
|
89
|
-
synchronize(opts) do |conn|
|
|
93
|
+
synchronize(opts[:server]) do |conn|
|
|
90
94
|
if h = @transactions[conn]
|
|
91
95
|
raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
|
|
92
96
|
(h[:after_rollback] ||= []) << block
|
data/lib/sequel/dataset/graph.rb
CHANGED
|
@@ -113,11 +113,13 @@ module Sequel
|
|
|
113
113
|
add_table = options[:select] == false ? false : true
|
|
114
114
|
# Whether to add the columns to the list of column aliases
|
|
115
115
|
add_columns = !ds.opts.include?(:graph_aliases)
|
|
116
|
-
# columns to select
|
|
117
|
-
select = (opts[:select] || []).dup
|
|
118
116
|
|
|
119
117
|
# Setup the initial graph data structure if it doesn't exist
|
|
120
|
-
|
|
118
|
+
if graph = opts[:graph]
|
|
119
|
+
opts[:graph] = graph = graph.dup
|
|
120
|
+
select = opts[:select].dup
|
|
121
|
+
[:column_aliases, :table_aliases, :column_alias_num].each{|k| graph[k] = graph[k].dup}
|
|
122
|
+
else
|
|
121
123
|
master = alias_symbol(ds.first_source_alias)
|
|
122
124
|
raise_alias_error.call if master == table_alias
|
|
123
125
|
# Master hash storing all .graph related information
|
|
@@ -132,9 +134,33 @@ module Sequel
|
|
|
132
134
|
# aliased, but are not included if set_graph_aliases
|
|
133
135
|
# has been used.
|
|
134
136
|
if add_columns
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
if (select = @opts[:select]) && !select.empty? && !(select.length == 1 && (select.first.is_a?(SQL::ColumnAll)))
|
|
138
|
+
select = select.each do |sel|
|
|
139
|
+
column = case sel
|
|
140
|
+
when Symbol
|
|
141
|
+
_, c, a = split_symbol(sel)
|
|
142
|
+
(a || c).to_sym
|
|
143
|
+
when SQL::Identifier
|
|
144
|
+
sel.value.to_sym
|
|
145
|
+
when SQL::QualifiedIdentifier
|
|
146
|
+
column = sel.column
|
|
147
|
+
column = column.value if column.is_a?(SQL::Identifier)
|
|
148
|
+
column.to_sym
|
|
149
|
+
when SQL::AliasedExpression
|
|
150
|
+
column = sel.aliaz
|
|
151
|
+
column = column.value if column.is_a?(SQL::Identifier)
|
|
152
|
+
column.to_sym
|
|
153
|
+
else
|
|
154
|
+
raise Error, "can't figure out alias to use for graphing for #{sel.inspect}"
|
|
155
|
+
end
|
|
156
|
+
column_aliases[column] = [master, column]
|
|
157
|
+
end
|
|
158
|
+
select = qualified_expression(select, master)
|
|
159
|
+
else
|
|
160
|
+
select = columns.map do |column|
|
|
161
|
+
column_aliases[column] = [master, column]
|
|
162
|
+
SQL::QualifiedIdentifier.new(master, column)
|
|
163
|
+
end
|
|
138
164
|
end
|
|
139
165
|
end
|
|
140
166
|
end
|
|
@@ -174,7 +200,7 @@ module Sequel
|
|
|
174
200
|
select.push(identifier)
|
|
175
201
|
end
|
|
176
202
|
end
|
|
177
|
-
ds.select(*select)
|
|
203
|
+
add_columns ? ds.select(*select) : ds
|
|
178
204
|
end
|
|
179
205
|
|
|
180
206
|
# This allows you to manually specify the graph aliases to use
|
|
@@ -63,6 +63,9 @@ module Sequel
|
|
|
63
63
|
# The array/hash of bound variable placeholder names.
|
|
64
64
|
attr_accessor :prepared_args
|
|
65
65
|
|
|
66
|
+
# The dataset that created this prepared statement.
|
|
67
|
+
attr_accessor :orig_dataset
|
|
68
|
+
|
|
66
69
|
# The argument to supply to insert and update, which may use
|
|
67
70
|
# placeholders specified by prepared_args
|
|
68
71
|
attr_accessor :prepared_modify_values
|
|
@@ -72,6 +75,12 @@ module Sequel
|
|
|
72
75
|
def call(bind_vars={}, &block)
|
|
73
76
|
bind(bind_vars).run(&block)
|
|
74
77
|
end
|
|
78
|
+
|
|
79
|
+
# Send the columns to the original dataset, as calling it
|
|
80
|
+
# on the prepared statement can cause problems.
|
|
81
|
+
def columns
|
|
82
|
+
orig_dataset.columns
|
|
83
|
+
end
|
|
75
84
|
|
|
76
85
|
# Returns the SQL for the prepared statement, depending on
|
|
77
86
|
# the type of the statement and the prepared_modify_values.
|
|
@@ -80,7 +89,7 @@ module Sequel
|
|
|
80
89
|
when :select, :all
|
|
81
90
|
select_sql
|
|
82
91
|
when :first
|
|
83
|
-
limit
|
|
92
|
+
clone(:limit=>1).select_sql
|
|
84
93
|
when :insert_select
|
|
85
94
|
returning.insert_sql(*@prepared_modify_values)
|
|
86
95
|
when :insert
|
|
@@ -95,10 +104,14 @@ module Sequel
|
|
|
95
104
|
# Changes the values of symbols if they start with $ and
|
|
96
105
|
# prepared_args is present. If so, they are considered placeholders,
|
|
97
106
|
# and they are substituted using prepared_arg.
|
|
98
|
-
def
|
|
107
|
+
def literal_symbol_append(sql, v)
|
|
99
108
|
if @opts[:bind_vars] and match = PLACEHOLDER_RE.match(v.to_s)
|
|
100
109
|
s = match[1].to_sym
|
|
101
|
-
prepared_arg?(s)
|
|
110
|
+
if prepared_arg?(s)
|
|
111
|
+
literal_append(sql, prepared_arg(s))
|
|
112
|
+
else
|
|
113
|
+
sql << v.to_s
|
|
114
|
+
end
|
|
102
115
|
else
|
|
103
116
|
super
|
|
104
117
|
end
|
|
@@ -148,8 +161,8 @@ module Sequel
|
|
|
148
161
|
# Use a clone of the dataset extended with prepared statement
|
|
149
162
|
# support and using the same argument hash so that you can use
|
|
150
163
|
# bind variables/prepared arguments in subselects.
|
|
151
|
-
def
|
|
152
|
-
ps = ds.prepare(:select)
|
|
164
|
+
def subselect_sql_append(sql, ds)
|
|
165
|
+
ps = ds.clone(:append_sql=>sql).prepare(:select)
|
|
153
166
|
ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
|
|
154
167
|
ps.prepared_args = prepared_args
|
|
155
168
|
ps.prepared_sql
|
|
@@ -240,6 +253,7 @@ module Sequel
|
|
|
240
253
|
def to_prepared_statement(type, values=nil)
|
|
241
254
|
ps = bind
|
|
242
255
|
ps.extend(PreparedStatementMethods)
|
|
256
|
+
ps.orig_dataset = self
|
|
243
257
|
ps.prepared_type = type
|
|
244
258
|
ps.prepared_modify_values = values
|
|
245
259
|
ps
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -61,9 +61,8 @@ module Sequel
|
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
columns = columns.map{|k| literal(String === k ? k.to_sym : k)}
|
|
65
64
|
if values.is_a?(Array) && values.empty? && !insert_supports_empty_values?
|
|
66
|
-
columns = [
|
|
65
|
+
columns = [columns().last]
|
|
67
66
|
values = ['DEFAULT'.lit]
|
|
68
67
|
end
|
|
69
68
|
clone(:columns=>columns, :values=>values)._insert_sql
|
|
@@ -79,41 +78,47 @@ module Sequel
|
|
|
79
78
|
# DB[:items].literal(:x + 1 > :y) => "((x + 1) > y)"
|
|
80
79
|
#
|
|
81
80
|
# If an unsupported object is given, an +Error+ is raised.
|
|
82
|
-
def
|
|
81
|
+
def literal_append(sql, v)
|
|
83
82
|
case v
|
|
84
|
-
when String
|
|
85
|
-
return v if v.is_a?(LiteralString)
|
|
86
|
-
v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
|
|
87
83
|
when Symbol
|
|
88
|
-
|
|
84
|
+
literal_symbol_append(sql, v)
|
|
85
|
+
when String
|
|
86
|
+
case v
|
|
87
|
+
when LiteralString
|
|
88
|
+
sql << v
|
|
89
|
+
when SQL::Blob
|
|
90
|
+
literal_blob_append(sql, v)
|
|
91
|
+
else
|
|
92
|
+
literal_string_append(sql, v)
|
|
93
|
+
end
|
|
89
94
|
when Integer
|
|
90
|
-
literal_integer(v)
|
|
95
|
+
sql << literal_integer(v)
|
|
91
96
|
when Hash
|
|
92
|
-
|
|
97
|
+
literal_hash_append(sql, v)
|
|
93
98
|
when SQL::Expression
|
|
94
|
-
|
|
99
|
+
literal_expression_append(sql, v)
|
|
95
100
|
when Float
|
|
96
|
-
literal_float(v)
|
|
101
|
+
sql << literal_float(v)
|
|
97
102
|
when BigDecimal
|
|
98
|
-
literal_big_decimal(v)
|
|
103
|
+
sql << literal_big_decimal(v)
|
|
99
104
|
when NilClass
|
|
100
|
-
literal_nil
|
|
105
|
+
sql << literal_nil
|
|
101
106
|
when TrueClass
|
|
102
|
-
literal_true
|
|
107
|
+
sql << literal_true
|
|
103
108
|
when FalseClass
|
|
104
|
-
literal_false
|
|
109
|
+
sql << literal_false
|
|
105
110
|
when Array
|
|
106
|
-
|
|
111
|
+
literal_array_append(sql, v)
|
|
107
112
|
when Time
|
|
108
|
-
v.is_a?(SQLTime) ? literal_sqltime(v) : literal_time(v)
|
|
113
|
+
sql << (v.is_a?(SQLTime) ? literal_sqltime(v) : literal_time(v))
|
|
109
114
|
when DateTime
|
|
110
|
-
literal_datetime(v)
|
|
115
|
+
sql << literal_datetime(v)
|
|
111
116
|
when Date
|
|
112
|
-
literal_date(v)
|
|
117
|
+
sql << literal_date(v)
|
|
113
118
|
when Dataset
|
|
114
|
-
|
|
119
|
+
literal_dataset_append(sql, v)
|
|
115
120
|
else
|
|
116
|
-
|
|
121
|
+
literal_other_append(sql, v)
|
|
117
122
|
end
|
|
118
123
|
end
|
|
119
124
|
|
|
@@ -177,84 +182,234 @@ module Sequel
|
|
|
177
182
|
clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
|
|
178
183
|
end
|
|
179
184
|
|
|
185
|
+
ALL = ' ALL'.freeze
|
|
180
186
|
AND_SEPARATOR = " AND ".freeze
|
|
187
|
+
APOS = "'".freeze
|
|
188
|
+
APOS_RE = /'/.freeze
|
|
189
|
+
ARRAY_EMPTY = '(NULL)'.freeze
|
|
190
|
+
AS = ' AS '.freeze
|
|
191
|
+
ASC = ' ASC'.freeze
|
|
192
|
+
BACKSLASH_RE = /\\/.freeze
|
|
181
193
|
BOOL_FALSE = "'f'".freeze
|
|
182
194
|
BOOL_TRUE = "'t'".freeze
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
195
|
+
BRACKET_CLOSE = ']'.freeze
|
|
196
|
+
BRACKET_OPEN = '['.freeze
|
|
197
|
+
CASE_ELSE = " ELSE ".freeze
|
|
198
|
+
CASE_END = " END)".freeze
|
|
199
|
+
CASE_OPEN = '(CASE'.freeze
|
|
200
|
+
CASE_THEN = " THEN ".freeze
|
|
201
|
+
CASE_WHEN = " WHEN ".freeze
|
|
202
|
+
CAST_OPEN = 'CAST('.freeze
|
|
203
|
+
COLUMN_ALL = '.*'.freeze
|
|
204
|
+
COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
|
|
205
|
+
COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
|
|
206
|
+
COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
|
|
207
|
+
COMMA = ', '.freeze
|
|
208
|
+
COMMA_SEPARATOR = COMMA
|
|
209
|
+
CONDITION_FALSE = '(1 = 0)'.freeze
|
|
210
|
+
CONDITION_TRUE = '(1 = 1)'.freeze
|
|
187
211
|
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
|
|
188
212
|
COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
|
|
189
213
|
DATASET_ALIAS_BASE_NAME = 't'.freeze
|
|
214
|
+
DEFAULT_VALUES = " DEFAULT VALUES".freeze
|
|
215
|
+
DELETE = 'DELETE'.freeze
|
|
216
|
+
DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'delete from where')
|
|
217
|
+
DESC = ' DESC'.freeze
|
|
218
|
+
DISTINCT = " DISTINCT".freeze
|
|
219
|
+
DOT = '.'.freeze
|
|
220
|
+
DOUBLE_APOS = "''".freeze
|
|
221
|
+
DOUBLE_QUOTE = '""'.freeze
|
|
222
|
+
EQUAL = ' = '.freeze
|
|
223
|
+
EXTRACT = 'extract('.freeze
|
|
190
224
|
FOR_UPDATE = ' FOR UPDATE'.freeze
|
|
225
|
+
FORMAT_DATE = "'%Y-%m-%d'".freeze
|
|
226
|
+
FORMAT_DATE_STANDARD = "DATE '%Y-%m-%d'".freeze
|
|
227
|
+
FORMAT_OFFSET = "%+03i%02i".freeze
|
|
228
|
+
FORMAT_TIMESTAMP_RE = /%[Nz]/.freeze
|
|
229
|
+
FORMAT_TIMESTAMP_USEC = ".%06d".freeze
|
|
230
|
+
FORMAT_USEC = '%N'.freeze
|
|
231
|
+
FRAME_ALL = "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING".freeze
|
|
232
|
+
FRAME_ROWS = "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW".freeze
|
|
233
|
+
FROM = ' FROM '.freeze
|
|
234
|
+
FUNCTION_EMPTY = '()'.freeze
|
|
235
|
+
GROUP_BY = " GROUP BY ".freeze
|
|
236
|
+
HAVING = " HAVING ".freeze
|
|
237
|
+
INSERT = "INSERT".freeze
|
|
238
|
+
INSERT_CLAUSE_METHODS = clause_methods(:insert, %w'insert into columns values')
|
|
239
|
+
INTO = " INTO ".freeze
|
|
191
240
|
IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
|
|
192
241
|
IS_OPERATORS = ::Sequel::SQL::ComplexExpression::IS_OPERATORS
|
|
242
|
+
LIMIT = " LIMIT ".freeze
|
|
193
243
|
N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
|
|
244
|
+
NOT_SPACE = 'NOT '.freeze
|
|
194
245
|
NULL = "NULL".freeze
|
|
246
|
+
NULLS_FIRST = " NULLS FIRST".freeze
|
|
247
|
+
NULLS_LAST = " NULLS LAST".freeze
|
|
248
|
+
OFFSET = " OFFSET ".freeze
|
|
249
|
+
ON = ' ON '.freeze
|
|
250
|
+
ON_PAREN = " ON (".freeze
|
|
251
|
+
ORDER_BY = " ORDER BY ".freeze
|
|
252
|
+
ORDER_BY_NS = "ORDER BY ".freeze
|
|
253
|
+
OVER = ' OVER '.freeze
|
|
254
|
+
PAREN_CLOSE = ')'.freeze
|
|
255
|
+
PAREN_OPEN = '('.freeze
|
|
256
|
+
PAREN_SPACE_OPEN = ' ('.freeze
|
|
257
|
+
PARTITION_BY = "PARTITION BY ".freeze
|
|
258
|
+
QUAD_BACKSLASH = "\\\\\\\\".freeze
|
|
195
259
|
QUALIFY_KEYS = [:select, :where, :having, :order, :group]
|
|
196
260
|
QUESTION_MARK = '?'.freeze
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
261
|
+
QUESTION_MARK_RE = /\?/.freeze
|
|
262
|
+
QUOTE = '"'.freeze
|
|
263
|
+
QUOTE_RE = /"/.freeze
|
|
264
|
+
RETURNING = " RETURNING ".freeze
|
|
265
|
+
SELECT = 'SELECT'.freeze
|
|
266
|
+
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'with select distinct columns from join where group having compounds order limit lock')
|
|
267
|
+
SET = ' SET '.freeze
|
|
268
|
+
SPACE = ' '.freeze
|
|
269
|
+
SQL_WITH = "WITH ".freeze
|
|
270
|
+
TILDE = '~'.freeze
|
|
201
271
|
TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S%N%z'".freeze
|
|
202
272
|
STANDARD_TIMESTAMP_FORMAT = "TIMESTAMP #{TIMESTAMP_FORMAT}".freeze
|
|
203
273
|
TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
|
|
274
|
+
UNDERSCORE = '_'.freeze
|
|
275
|
+
UPDATE = 'UPDATE'.freeze
|
|
276
|
+
UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'update table set where')
|
|
277
|
+
USING = ' USING ('.freeze
|
|
278
|
+
VALUES = " VALUES ".freeze
|
|
279
|
+
WHERE = " WHERE ".freeze
|
|
204
280
|
WILDCARD = LiteralString.new('*').freeze
|
|
205
|
-
|
|
281
|
+
|
|
282
|
+
PUBLIC_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
|
|
283
|
+
literal
|
|
284
|
+
aliased_expression_sql
|
|
285
|
+
array_sql
|
|
286
|
+
boolean_constant_sql
|
|
287
|
+
case_expression_sql
|
|
288
|
+
cast_sql
|
|
289
|
+
column_all_sql
|
|
290
|
+
complex_expression_sql
|
|
291
|
+
constant_sql
|
|
292
|
+
function_sql
|
|
293
|
+
join_clause_sql
|
|
294
|
+
join_on_clause_sql
|
|
295
|
+
join_using_clause_sql
|
|
296
|
+
negative_boolean_constant_sql
|
|
297
|
+
ordered_expression_sql
|
|
298
|
+
placeholder_literal_string_sql
|
|
299
|
+
qualified_identifier_sql
|
|
300
|
+
quote_identifier
|
|
301
|
+
quote_schema_table
|
|
302
|
+
quoted_identifier
|
|
303
|
+
subscript_sql
|
|
304
|
+
window_sql
|
|
305
|
+
window_function_sql
|
|
306
|
+
END
|
|
307
|
+
PRIVATE_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
|
|
308
|
+
argument_list
|
|
309
|
+
as_sql
|
|
310
|
+
column_list
|
|
311
|
+
compound_dataset_sql
|
|
312
|
+
expression_list
|
|
313
|
+
literal_array
|
|
314
|
+
literal_blob
|
|
315
|
+
literal_dataset
|
|
316
|
+
literal_expression
|
|
317
|
+
literal_hash
|
|
318
|
+
literal_other
|
|
319
|
+
literal_string
|
|
320
|
+
literal_symbol
|
|
321
|
+
source_list
|
|
322
|
+
subselect_sql
|
|
323
|
+
table_ref
|
|
324
|
+
END
|
|
325
|
+
def self.def_append_methods(meths)
|
|
326
|
+
meths.each do |meth|
|
|
327
|
+
class_eval(<<-END, __FILE__, __LINE__ + 1)
|
|
328
|
+
def #{meth}(*args, &block)
|
|
329
|
+
s = ''
|
|
330
|
+
#{meth}_append(s, *args, &block)
|
|
331
|
+
s
|
|
332
|
+
end
|
|
333
|
+
END
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
def_append_methods(PUBLIC_APPEND_METHODS + PRIVATE_APPEND_METHODS)
|
|
337
|
+
private *PRIVATE_APPEND_METHODS
|
|
206
338
|
|
|
207
339
|
# SQL fragment for AliasedExpression
|
|
208
|
-
def
|
|
209
|
-
|
|
340
|
+
def aliased_expression_sql_append(sql, ae)
|
|
341
|
+
literal_append(sql, ae.expression)
|
|
342
|
+
as_sql_append(sql, ae.aliaz)
|
|
210
343
|
end
|
|
211
344
|
|
|
212
345
|
# SQL fragment for Array
|
|
213
|
-
def
|
|
214
|
-
a.empty?
|
|
346
|
+
def array_sql_append(sql, a)
|
|
347
|
+
if a.empty?
|
|
348
|
+
sql << ARRAY_EMPTY
|
|
349
|
+
else
|
|
350
|
+
sql << PAREN_OPEN
|
|
351
|
+
expression_list_append(sql, a)
|
|
352
|
+
sql << PAREN_CLOSE
|
|
353
|
+
end
|
|
215
354
|
end
|
|
216
355
|
|
|
217
356
|
# SQL fragment for BooleanConstants
|
|
218
|
-
def
|
|
357
|
+
def boolean_constant_sql_append(sql, constant)
|
|
219
358
|
if (constant == true || constant == false) && !supports_where_true?
|
|
220
|
-
constant == true ?
|
|
359
|
+
sql << (constant == true ? CONDITION_TRUE : CONDITION_FALSE)
|
|
221
360
|
else
|
|
222
|
-
|
|
361
|
+
literal_append(sql, constant)
|
|
223
362
|
end
|
|
224
363
|
end
|
|
225
364
|
|
|
226
365
|
# SQL fragment for CaseExpression
|
|
227
|
-
def
|
|
228
|
-
sql
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
sql
|
|
232
|
-
|
|
233
|
-
|
|
366
|
+
def case_expression_sql_append(sql, ce)
|
|
367
|
+
sql << CASE_OPEN
|
|
368
|
+
if ce.expression?
|
|
369
|
+
sql << SPACE
|
|
370
|
+
literal_append(sql, ce.expression)
|
|
371
|
+
end
|
|
372
|
+
w = CASE_WHEN
|
|
373
|
+
t = CASE_THEN
|
|
374
|
+
ce.conditions.each do |c,r|
|
|
375
|
+
sql << w
|
|
376
|
+
literal_append(sql, c)
|
|
377
|
+
sql << t
|
|
378
|
+
literal_append(sql, r)
|
|
379
|
+
end
|
|
380
|
+
sql << CASE_ELSE
|
|
381
|
+
literal_append(sql, ce.default)
|
|
382
|
+
sql << CASE_END
|
|
234
383
|
end
|
|
235
384
|
|
|
236
385
|
# SQL fragment for the SQL CAST expression
|
|
237
|
-
def
|
|
238
|
-
|
|
386
|
+
def cast_sql_append(sql, expr, type)
|
|
387
|
+
sql << CAST_OPEN
|
|
388
|
+
literal_append(sql, expr)
|
|
389
|
+
sql << AS << db.cast_type_literal(type).to_s
|
|
390
|
+
sql << PAREN_CLOSE
|
|
239
391
|
end
|
|
240
392
|
|
|
241
393
|
# SQL fragment for specifying all columns in a given table
|
|
242
|
-
def
|
|
243
|
-
|
|
394
|
+
def column_all_sql_append(sql, ca)
|
|
395
|
+
quote_schema_table_append(sql, ca.table)
|
|
396
|
+
sql << COLUMN_ALL
|
|
244
397
|
end
|
|
245
398
|
|
|
246
|
-
|
|
247
|
-
def complex_expression_sql(op, args)
|
|
399
|
+
def complex_expression_sql_append(sql, op, args)
|
|
248
400
|
case op
|
|
249
401
|
when *IS_OPERATORS
|
|
250
402
|
r = args.at(1)
|
|
251
403
|
if r.nil? || supports_is_true?
|
|
252
404
|
raise(InvalidOperation, 'Invalid argument used for IS operator') unless v = IS_LITERALS[r]
|
|
253
|
-
|
|
405
|
+
sql << PAREN_OPEN
|
|
406
|
+
literal_append(sql, args.at(0))
|
|
407
|
+
sql << SPACE << op.to_s << SPACE
|
|
408
|
+
sql << v << PAREN_CLOSE
|
|
254
409
|
elsif op == :IS
|
|
255
|
-
|
|
410
|
+
complex_expression_sql_append(sql, :"=", args)
|
|
256
411
|
else
|
|
257
|
-
|
|
412
|
+
complex_expression_sql_append(sql, :OR, [SQL::BooleanExpression.new(:"!=", *args), SQL::BooleanExpression.new(:IS, args.at(0), nil)])
|
|
258
413
|
end
|
|
259
414
|
when :IN, :"NOT IN"
|
|
260
415
|
cols = args.at(0)
|
|
@@ -267,147 +422,228 @@ module Sequel
|
|
|
267
422
|
if col_array
|
|
268
423
|
if empty_val_array
|
|
269
424
|
if op == :IN
|
|
270
|
-
|
|
425
|
+
literal_append(sql, SQL::BooleanExpression.from_value_pairs(cols.to_a.map{|x| [x, x]}, :AND, true))
|
|
271
426
|
else
|
|
272
|
-
|
|
427
|
+
literal_append(sql, 1=>1)
|
|
273
428
|
end
|
|
274
429
|
elsif !supports_multiple_column_in?
|
|
275
430
|
if val_array
|
|
276
431
|
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]})})
|
|
277
|
-
|
|
432
|
+
literal_append(sql, op == :IN ? expr : ~expr)
|
|
278
433
|
else
|
|
279
434
|
old_vals = vals
|
|
280
435
|
vals = vals.naked if vals.is_a?(Sequel::Dataset)
|
|
281
436
|
vals = vals.to_a
|
|
282
437
|
val_cols = old_vals.columns
|
|
283
|
-
|
|
438
|
+
complex_expression_sql_append(sql, op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
|
|
284
439
|
end
|
|
285
440
|
else
|
|
286
441
|
# If the columns and values are both arrays, use array_sql instead of
|
|
287
442
|
# literal so that if values is an array of two element arrays, it
|
|
288
443
|
# will be treated as a value list instead of a condition specifier.
|
|
289
|
-
|
|
444
|
+
sql << PAREN_OPEN
|
|
445
|
+
literal_append(sql, cols)
|
|
446
|
+
sql << SPACE << op.to_s << SPACE
|
|
447
|
+
if val_array
|
|
448
|
+
array_sql_append(sql, vals)
|
|
449
|
+
else
|
|
450
|
+
literal_append(sql, vals)
|
|
451
|
+
end
|
|
452
|
+
sql << PAREN_CLOSE
|
|
290
453
|
end
|
|
291
454
|
else
|
|
292
455
|
if empty_val_array
|
|
293
456
|
if op == :IN
|
|
294
|
-
|
|
457
|
+
literal_append(sql, SQL::BooleanExpression.from_value_pairs([[cols, cols]], :AND, true))
|
|
295
458
|
else
|
|
296
|
-
|
|
459
|
+
literal_append(sql, 1=>1)
|
|
297
460
|
end
|
|
298
461
|
else
|
|
299
|
-
|
|
462
|
+
sql << PAREN_OPEN
|
|
463
|
+
literal_append(sql, cols)
|
|
464
|
+
sql << SPACE << op.to_s << SPACE
|
|
465
|
+
literal_append(sql, vals)
|
|
466
|
+
sql << PAREN_CLOSE
|
|
300
467
|
end
|
|
301
468
|
end
|
|
302
469
|
when *TWO_ARITY_OPERATORS
|
|
303
|
-
|
|
470
|
+
sql << PAREN_OPEN
|
|
471
|
+
literal_append(sql, args.at(0))
|
|
472
|
+
sql << SPACE << op.to_s << SPACE
|
|
473
|
+
literal_append(sql, args.at(1))
|
|
474
|
+
sql << PAREN_CLOSE
|
|
304
475
|
when *N_ARITY_OPERATORS
|
|
305
|
-
|
|
476
|
+
sql << PAREN_OPEN
|
|
477
|
+
c = false
|
|
478
|
+
op_str = " #{op} "
|
|
479
|
+
args.each do |a|
|
|
480
|
+
sql << op_str if c
|
|
481
|
+
literal_append(sql, a)
|
|
482
|
+
c ||= true
|
|
483
|
+
end
|
|
484
|
+
sql << PAREN_CLOSE
|
|
306
485
|
when :NOT
|
|
307
|
-
|
|
486
|
+
sql << NOT_SPACE
|
|
487
|
+
literal_append(sql, args.at(0))
|
|
308
488
|
when :NOOP
|
|
309
|
-
|
|
489
|
+
literal_append(sql, args.at(0))
|
|
310
490
|
when :'B~'
|
|
311
|
-
|
|
491
|
+
sql << TILDE
|
|
492
|
+
literal_append(sql, args.at(0))
|
|
312
493
|
when :extract
|
|
313
|
-
|
|
494
|
+
sql << EXTRACT << args.at(0).to_s << FROM
|
|
495
|
+
literal_append(sql, args.at(1))
|
|
496
|
+
sql << PAREN_CLOSE
|
|
314
497
|
else
|
|
315
498
|
raise(InvalidOperation, "invalid operator #{op}")
|
|
316
499
|
end
|
|
317
500
|
end
|
|
318
501
|
|
|
319
502
|
# SQL fragment for constants
|
|
320
|
-
def
|
|
321
|
-
constant.to_s
|
|
503
|
+
def constant_sql_append(sql, constant)
|
|
504
|
+
sql << constant.to_s
|
|
322
505
|
end
|
|
323
506
|
|
|
324
507
|
# SQL fragment specifying an SQL function call
|
|
325
|
-
def
|
|
508
|
+
def function_sql_append(sql, f)
|
|
509
|
+
sql << f.f.to_s
|
|
326
510
|
args = f.args
|
|
327
|
-
|
|
511
|
+
if args.empty?
|
|
512
|
+
sql << FUNCTION_EMPTY
|
|
513
|
+
else
|
|
514
|
+
literal_append(sql, args)
|
|
515
|
+
end
|
|
328
516
|
end
|
|
329
517
|
|
|
330
518
|
# SQL fragment specifying a JOIN clause without ON or USING.
|
|
331
|
-
def
|
|
519
|
+
def join_clause_sql_append(sql, jc)
|
|
332
520
|
table = jc.table
|
|
333
521
|
table_alias = jc.table_alias
|
|
334
522
|
table_alias = nil if table == table_alias
|
|
335
|
-
|
|
336
|
-
|
|
523
|
+
sql << SPACE << join_type_sql(jc.join_type) << SPACE
|
|
524
|
+
table_ref_append(sql, table)
|
|
525
|
+
as_sql_append(sql, table_alias) if table_alias
|
|
337
526
|
end
|
|
338
527
|
|
|
339
528
|
# SQL fragment specifying a JOIN clause with ON.
|
|
340
|
-
def
|
|
341
|
-
|
|
529
|
+
def join_on_clause_sql_append(sql, jc)
|
|
530
|
+
join_clause_sql_append(sql, jc)
|
|
531
|
+
sql << ON
|
|
532
|
+
literal_append(sql, filter_expr(jc.on))
|
|
342
533
|
end
|
|
343
534
|
|
|
344
535
|
# SQL fragment specifying a JOIN clause with USING.
|
|
345
|
-
def
|
|
346
|
-
|
|
536
|
+
def join_using_clause_sql_append(sql, jc)
|
|
537
|
+
join_clause_sql_append(sql, jc)
|
|
538
|
+
sql << USING
|
|
539
|
+
column_list_append(sql, jc.using)
|
|
540
|
+
sql << PAREN_CLOSE
|
|
347
541
|
end
|
|
348
542
|
|
|
349
543
|
# SQL fragment for NegativeBooleanConstants
|
|
350
|
-
def
|
|
351
|
-
|
|
544
|
+
def negative_boolean_constant_sql_append(sql, constant)
|
|
545
|
+
sql << NOT_SPACE
|
|
546
|
+
boolean_constant_sql_append(sql, constant)
|
|
352
547
|
end
|
|
353
548
|
|
|
354
549
|
# SQL fragment for the ordered expression, used in the ORDER BY
|
|
355
550
|
# clause.
|
|
356
|
-
def
|
|
357
|
-
|
|
551
|
+
def ordered_expression_sql_append(sql, oe)
|
|
552
|
+
literal_append(sql, oe.expression)
|
|
553
|
+
sql << (oe.descending ? DESC : ASC)
|
|
358
554
|
case oe.nulls
|
|
359
555
|
when :first
|
|
360
|
-
|
|
556
|
+
sql << NULLS_FIRST
|
|
361
557
|
when :last
|
|
362
|
-
|
|
363
|
-
else
|
|
364
|
-
s
|
|
558
|
+
sql << NULLS_LAST
|
|
365
559
|
end
|
|
366
560
|
end
|
|
367
561
|
|
|
368
562
|
# SQL fragment for a literal string with placeholders
|
|
369
|
-
def
|
|
563
|
+
def placeholder_literal_string_sql_append(sql, pls)
|
|
370
564
|
args = pls.args
|
|
371
|
-
|
|
565
|
+
sql << PAREN_OPEN if pls.parens
|
|
566
|
+
if args.is_a?(Hash)
|
|
372
567
|
re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
|
|
373
|
-
|
|
568
|
+
if RUBY_VERSION >= '1.8.7'
|
|
569
|
+
str = pls.str
|
|
570
|
+
loop do
|
|
571
|
+
previous, q, str = str.partition(re)
|
|
572
|
+
sql << previous
|
|
573
|
+
literal_append(sql, args[($1||q[1..-1].to_s).to_sym]) unless q.empty?
|
|
574
|
+
break if str.empty?
|
|
575
|
+
end
|
|
576
|
+
else
|
|
577
|
+
sql << pls.str.gsub(re){literal(args[$1.to_sym])}
|
|
578
|
+
end
|
|
374
579
|
else
|
|
375
580
|
i = -1
|
|
376
|
-
|
|
581
|
+
if RUBY_VERSION >= '1.8.7'
|
|
582
|
+
str = pls.str
|
|
583
|
+
loop do
|
|
584
|
+
previous, q, str = str.partition(QUESTION_MARK)
|
|
585
|
+
sql << previous
|
|
586
|
+
literal_append(sql, args.at(i+=1)) unless q.empty?
|
|
587
|
+
break if str.empty?
|
|
588
|
+
end
|
|
589
|
+
else
|
|
590
|
+
sql << pls.str.gsub(QUESTION_MARK_RE){literal(args.at(i+=1))}
|
|
591
|
+
end
|
|
377
592
|
end
|
|
378
|
-
|
|
379
|
-
s
|
|
593
|
+
sql << PAREN_CLOSE if pls.parens
|
|
380
594
|
end
|
|
381
595
|
|
|
382
596
|
# SQL fragment for the qualifed identifier, specifying
|
|
383
597
|
# a table and a column (or schema and table).
|
|
384
|
-
def
|
|
385
|
-
|
|
598
|
+
def qualified_identifier_sql_append(sql, qcr)
|
|
599
|
+
case t = qcr.table
|
|
600
|
+
when Symbol, SQL::QualifiedIdentifier, SQL::Identifier
|
|
601
|
+
literal_append(sql, t)
|
|
602
|
+
else
|
|
603
|
+
quote_identifier_append(sql, t)
|
|
604
|
+
end
|
|
605
|
+
sql << DOT
|
|
606
|
+
case c = qcr.column
|
|
607
|
+
when Symbol, SQL::QualifiedIdentifier, SQL::Identifier
|
|
608
|
+
literal_append(sql, c)
|
|
609
|
+
else
|
|
610
|
+
quote_identifier_append(sql, c)
|
|
611
|
+
end
|
|
386
612
|
end
|
|
387
613
|
|
|
388
614
|
# Adds quoting to identifiers (columns and tables). If identifiers are not
|
|
389
615
|
# being quoted, returns name as a string. If identifiers are being quoted
|
|
390
616
|
# quote the name with quoted_identifier.
|
|
391
|
-
def
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
617
|
+
def quote_identifier_append(sql, name)
|
|
618
|
+
if name.is_a?(LiteralString)
|
|
619
|
+
sql << name
|
|
620
|
+
else
|
|
621
|
+
name = name.value if name.is_a?(SQL::Identifier)
|
|
622
|
+
name = input_identifier(name)
|
|
623
|
+
if quote_identifiers?
|
|
624
|
+
quoted_identifier_append(sql, name)
|
|
625
|
+
else
|
|
626
|
+
sql << name
|
|
627
|
+
end
|
|
628
|
+
end
|
|
397
629
|
end
|
|
398
630
|
|
|
399
631
|
# Separates the schema from the table and returns a string with them
|
|
400
632
|
# quoted (if quoting identifiers)
|
|
401
|
-
def
|
|
633
|
+
def quote_schema_table_append(sql, table)
|
|
402
634
|
schema, table = schema_and_table(table)
|
|
403
|
-
|
|
635
|
+
if schema
|
|
636
|
+
quote_identifier_append(sql, schema)
|
|
637
|
+
sql << DOT
|
|
638
|
+
end
|
|
639
|
+
quote_identifier_append(sql, table)
|
|
404
640
|
end
|
|
405
641
|
|
|
406
642
|
# This method quotes the given name with the SQL standard double quote.
|
|
407
643
|
# should be overridden by subclasses to provide quoting not matching the
|
|
408
644
|
# SQL standard, such as backtick (used by MySQL and SQLite).
|
|
409
|
-
def
|
|
410
|
-
|
|
645
|
+
def quoted_identifier_append(sql, name)
|
|
646
|
+
sql << QUOTE << name.to_s.gsub(QUOTE_RE, DOUBLE_QUOTE) << QUOTE
|
|
411
647
|
end
|
|
412
648
|
|
|
413
649
|
# Split the schema information from the table
|
|
@@ -429,34 +665,59 @@ module Sequel
|
|
|
429
665
|
end
|
|
430
666
|
|
|
431
667
|
# SQL fragment for specifying subscripts (SQL array accesses)
|
|
432
|
-
def
|
|
433
|
-
|
|
668
|
+
def subscript_sql_append(sql, s)
|
|
669
|
+
literal_append(sql, s.f)
|
|
670
|
+
sql << BRACKET_OPEN
|
|
671
|
+
expression_list_append(sql, s.sub)
|
|
672
|
+
sql << BRACKET_CLOSE
|
|
434
673
|
end
|
|
435
674
|
|
|
436
675
|
# The SQL fragment for the given window's options.
|
|
437
|
-
def
|
|
676
|
+
def window_sql_append(sql, opts)
|
|
438
677
|
raise(Error, 'This dataset does not support window functions') unless supports_window_functions?
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
678
|
+
sql << PAREN_OPEN
|
|
679
|
+
window, part, order, frame = opts.values_at(:window, :partition, :order, :frame)
|
|
680
|
+
space = false
|
|
681
|
+
space_s = SPACE
|
|
682
|
+
if window
|
|
683
|
+
literal_append(sql, window)
|
|
684
|
+
space = true
|
|
685
|
+
end
|
|
686
|
+
if part
|
|
687
|
+
sql << space_s if space
|
|
688
|
+
sql << PARTITION_BY
|
|
689
|
+
expression_list_append(sql, Array(part))
|
|
690
|
+
space = true
|
|
691
|
+
end
|
|
692
|
+
if order
|
|
693
|
+
sql << space_s if space
|
|
694
|
+
sql << ORDER_BY_NS
|
|
695
|
+
expression_list_append(sql, Array(order))
|
|
696
|
+
space = true
|
|
697
|
+
end
|
|
698
|
+
case frame
|
|
443
699
|
when nil
|
|
444
|
-
|
|
700
|
+
# nothing
|
|
445
701
|
when :all
|
|
446
|
-
|
|
702
|
+
sql << space_s if space
|
|
703
|
+
sql << FRAME_ALL
|
|
447
704
|
when :rows
|
|
448
|
-
|
|
705
|
+
sql << space_s if space
|
|
706
|
+
sql << FRAME_ROWS
|
|
449
707
|
when String
|
|
450
|
-
|
|
708
|
+
sql << space_s if space
|
|
709
|
+
sql << frame
|
|
451
710
|
else
|
|
452
711
|
raise Error, "invalid window frame clause, should be :all, :rows, a string, or nil"
|
|
453
712
|
end
|
|
454
|
-
|
|
713
|
+
sql << PAREN_CLOSE
|
|
455
714
|
end
|
|
456
715
|
|
|
457
716
|
# The SQL fragment for the given window function's function and window.
|
|
458
|
-
def
|
|
459
|
-
|
|
717
|
+
def window_function_sql_append(sql, function, window)
|
|
718
|
+
literal_append(sql, function)
|
|
719
|
+
sql << OVER
|
|
720
|
+
literal_append(sql, window)
|
|
460
721
|
end
|
|
461
722
|
|
|
462
723
|
protected
|
|
@@ -528,14 +789,20 @@ module Sequel
|
|
|
528
789
|
options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
|
|
529
790
|
end
|
|
530
791
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
792
|
+
def argument_list_append(sql, args)
|
|
793
|
+
c = false
|
|
794
|
+
comma = COMMA
|
|
795
|
+
args.each do |a|
|
|
796
|
+
sql << comma if c
|
|
797
|
+
sql << a.to_s
|
|
798
|
+
c ||= true
|
|
799
|
+
end
|
|
534
800
|
end
|
|
535
801
|
|
|
536
802
|
# SQL fragment for specifying an alias. expression should already be literalized.
|
|
537
|
-
def
|
|
538
|
-
|
|
803
|
+
def as_sql_append(sql, aliaz)
|
|
804
|
+
sql << AS
|
|
805
|
+
quote_identifier_append(sql, aliaz)
|
|
539
806
|
end
|
|
540
807
|
|
|
541
808
|
# Raise an InvalidOperation exception if deletion is not allowed
|
|
@@ -547,17 +814,21 @@ module Sequel
|
|
|
547
814
|
|
|
548
815
|
# Prepare an SQL statement by calling all clause methods for the given statement type.
|
|
549
816
|
def clause_sql(type)
|
|
550
|
-
sql =
|
|
817
|
+
sql = @opts[:append_sql] || ''
|
|
551
818
|
send("#{type}_clause_methods").each{|x| send(x, sql)}
|
|
552
819
|
sql
|
|
553
820
|
end
|
|
554
821
|
|
|
555
822
|
# Converts an array of column names into a comma seperated string of
|
|
556
823
|
# column names. If the array is empty, a wildcard (*) is returned.
|
|
557
|
-
def
|
|
558
|
-
(columns.nil? || columns.empty?)
|
|
824
|
+
def column_list_append(sql, columns)
|
|
825
|
+
if (columns.nil? || columns.empty?)
|
|
826
|
+
sql << WILDCARD
|
|
827
|
+
else
|
|
828
|
+
expression_list_append(sql, columns)
|
|
829
|
+
end
|
|
559
830
|
end
|
|
560
|
-
|
|
831
|
+
|
|
561
832
|
# Yield each two pair of arguments to the block, which should
|
|
562
833
|
# return a string representing the SQL code for those
|
|
563
834
|
# two arguments. If more than 2 arguments are provided, all
|
|
@@ -576,8 +847,8 @@ module Sequel
|
|
|
576
847
|
end
|
|
577
848
|
|
|
578
849
|
# The SQL to use for the dataset used in a UNION/INTERSECT/EXCEPT clause.
|
|
579
|
-
def
|
|
580
|
-
|
|
850
|
+
def compound_dataset_sql_append(sql, ds)
|
|
851
|
+
subselect_sql_append(sql, ds)
|
|
581
852
|
end
|
|
582
853
|
|
|
583
854
|
# The alias to use for datasets, takes a number to make sure the name is unique.
|
|
@@ -590,10 +861,20 @@ module Sequel
|
|
|
590
861
|
DELETE_CLAUSE_METHODS
|
|
591
862
|
end
|
|
592
863
|
|
|
864
|
+
def delete_delete_sql(sql)
|
|
865
|
+
sql << DELETE
|
|
866
|
+
end
|
|
867
|
+
|
|
593
868
|
# Converts an array of expressions into a comma separated string of
|
|
594
869
|
# expressions.
|
|
595
|
-
def
|
|
596
|
-
|
|
870
|
+
def expression_list_append(sql, columns)
|
|
871
|
+
c = false
|
|
872
|
+
co = COMMA
|
|
873
|
+
columns.each do |col|
|
|
874
|
+
sql << co if c
|
|
875
|
+
literal_append(sql, col)
|
|
876
|
+
c ||= true
|
|
877
|
+
end
|
|
597
878
|
end
|
|
598
879
|
|
|
599
880
|
# The strftime format to use when literalizing the time.
|
|
@@ -607,8 +888,8 @@ module Sequel
|
|
|
607
888
|
# of hours and minutes.
|
|
608
889
|
def format_timestamp(v)
|
|
609
890
|
v2 = db.from_application_timestamp(v)
|
|
610
|
-
fmt = default_timestamp_format.gsub(
|
|
611
|
-
if m ==
|
|
891
|
+
fmt = default_timestamp_format.gsub(FORMAT_TIMESTAMP_RE) do |m|
|
|
892
|
+
if m == FORMAT_USEC
|
|
612
893
|
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
|
|
613
894
|
else
|
|
614
895
|
if supports_timestamp_timezones?
|
|
@@ -624,13 +905,13 @@ module Sequel
|
|
|
624
905
|
|
|
625
906
|
# Return the SQL timestamp fragment to use for the timezone offset.
|
|
626
907
|
def format_timestamp_offset(hour, minute)
|
|
627
|
-
sprintf(
|
|
908
|
+
sprintf(FORMAT_OFFSET, hour, minute)
|
|
628
909
|
end
|
|
629
910
|
|
|
630
911
|
# Return the SQL timestamp fragment to use for the fractional time part.
|
|
631
912
|
# Should start with the decimal point. Uses 6 decimal places by default.
|
|
632
913
|
def format_timestamp_usec(usec)
|
|
633
|
-
sprintf(
|
|
914
|
+
sprintf(FORMAT_TIMESTAMP_USEC, usec)
|
|
634
915
|
end
|
|
635
916
|
|
|
636
917
|
# Modify the identifier returned from the database based on the
|
|
@@ -641,7 +922,8 @@ module Sequel
|
|
|
641
922
|
|
|
642
923
|
# SQL fragment specifying the table to insert INTO
|
|
643
924
|
def insert_into_sql(sql)
|
|
644
|
-
sql <<
|
|
925
|
+
sql << INTO
|
|
926
|
+
source_list_append(sql, @opts[:from])
|
|
645
927
|
end
|
|
646
928
|
|
|
647
929
|
# The order of methods to call to build the INSERT SQL statement
|
|
@@ -652,18 +934,42 @@ module Sequel
|
|
|
652
934
|
# SQL fragment specifying the columns to insert into
|
|
653
935
|
def insert_columns_sql(sql)
|
|
654
936
|
columns = opts[:columns]
|
|
655
|
-
|
|
937
|
+
if columns && !columns.empty?
|
|
938
|
+
sql << PAREN_SPACE_OPEN
|
|
939
|
+
c = false
|
|
940
|
+
co = COMMA
|
|
941
|
+
columns.each do |col|
|
|
942
|
+
sql << co if c
|
|
943
|
+
if col.is_a?(String) && !col.is_a?(LiteralString)
|
|
944
|
+
quote_identifier_append(sql, col)
|
|
945
|
+
else
|
|
946
|
+
literal_append(sql, col)
|
|
947
|
+
end
|
|
948
|
+
c ||= true
|
|
949
|
+
end
|
|
950
|
+
sql << PAREN_CLOSE
|
|
951
|
+
end
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
def insert_insert_sql(sql)
|
|
955
|
+
sql << INSERT
|
|
656
956
|
end
|
|
657
957
|
|
|
658
958
|
# SQL fragment specifying the values to insert.
|
|
659
959
|
def insert_values_sql(sql)
|
|
660
960
|
case values = opts[:values]
|
|
661
961
|
when Array
|
|
662
|
-
|
|
962
|
+
if values.empty?
|
|
963
|
+
sql << DEFAULT_VALUES
|
|
964
|
+
else
|
|
965
|
+
sql << VALUES
|
|
966
|
+
literal_append(sql, values)
|
|
967
|
+
end
|
|
663
968
|
when Dataset
|
|
664
|
-
sql <<
|
|
969
|
+
sql << SPACE
|
|
970
|
+
subselect_sql_append(sql, values)
|
|
665
971
|
when LiteralString
|
|
666
|
-
sql <<
|
|
972
|
+
sql << SPACE << values
|
|
667
973
|
else
|
|
668
974
|
raise Error, "Unsupported INSERT values type, should be an Array or Dataset: #{values.inspect}"
|
|
669
975
|
end
|
|
@@ -672,7 +978,8 @@ module Sequel
|
|
|
672
978
|
# SQL fragment specifying the values to return.
|
|
673
979
|
def insert_returning_sql(sql)
|
|
674
980
|
if opts.has_key?(:returning)
|
|
675
|
-
sql <<
|
|
981
|
+
sql << RETURNING
|
|
982
|
+
column_list_append(sql, Array(opts[:returning]))
|
|
676
983
|
end
|
|
677
984
|
end
|
|
678
985
|
alias delete_returning_sql insert_returning_sql
|
|
@@ -681,7 +988,7 @@ module Sequel
|
|
|
681
988
|
# SQL fragment specifying a JOIN type, converts underscores to
|
|
682
989
|
# spaces and upcases.
|
|
683
990
|
def join_type_sql(join_type)
|
|
684
|
-
"#{join_type.to_s.gsub(
|
|
991
|
+
"#{join_type.to_s.gsub(UNDERSCORE, SPACE).upcase} JOIN"
|
|
685
992
|
end
|
|
686
993
|
|
|
687
994
|
# Whether this dataset is a joined dataset
|
|
@@ -690,8 +997,12 @@ module Sequel
|
|
|
690
997
|
end
|
|
691
998
|
|
|
692
999
|
# SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
|
|
693
|
-
def
|
|
694
|
-
Sequel.condition_specifier?(v)
|
|
1000
|
+
def literal_array_append(sql, v)
|
|
1001
|
+
if Sequel.condition_specifier?(v)
|
|
1002
|
+
literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
|
|
1003
|
+
else
|
|
1004
|
+
array_sql_append(sql, v)
|
|
1005
|
+
end
|
|
695
1006
|
end
|
|
696
1007
|
|
|
697
1008
|
# SQL fragment for BigDecimal
|
|
@@ -701,18 +1012,24 @@ module Sequel
|
|
|
701
1012
|
end
|
|
702
1013
|
|
|
703
1014
|
# SQL fragment for SQL::Blob
|
|
704
|
-
def
|
|
705
|
-
|
|
1015
|
+
def literal_blob_append(sql, v)
|
|
1016
|
+
literal_string_append(sql, v)
|
|
706
1017
|
end
|
|
707
1018
|
|
|
708
1019
|
# SQL fragment for Dataset. Does a subselect inside parantheses.
|
|
709
|
-
def
|
|
710
|
-
|
|
1020
|
+
def literal_dataset_append(sql, v)
|
|
1021
|
+
sql << PAREN_OPEN
|
|
1022
|
+
subselect_sql_append(sql, v)
|
|
1023
|
+
sql << PAREN_CLOSE
|
|
711
1024
|
end
|
|
712
1025
|
|
|
713
1026
|
# SQL fragment for Date, using the ISO8601 format.
|
|
714
1027
|
def literal_date(v)
|
|
715
|
-
|
|
1028
|
+
if requires_sql_standard_datetimes?
|
|
1029
|
+
v.strftime(FORMAT_DATE_STANDARD)
|
|
1030
|
+
else
|
|
1031
|
+
v.strftime(FORMAT_DATE)
|
|
1032
|
+
end
|
|
716
1033
|
end
|
|
717
1034
|
|
|
718
1035
|
# SQL fragment for DateTime
|
|
@@ -721,8 +1038,8 @@ module Sequel
|
|
|
721
1038
|
end
|
|
722
1039
|
|
|
723
1040
|
# SQL fragment for SQL::Expression, result depends on the specific type of expression.
|
|
724
|
-
def
|
|
725
|
-
v.
|
|
1041
|
+
def literal_expression_append(sql, v)
|
|
1042
|
+
v.to_s_append(self, sql)
|
|
726
1043
|
end
|
|
727
1044
|
|
|
728
1045
|
# SQL fragment for false
|
|
@@ -736,8 +1053,8 @@ module Sequel
|
|
|
736
1053
|
end
|
|
737
1054
|
|
|
738
1055
|
# SQL fragment for Hash, treated as an expression
|
|
739
|
-
def
|
|
740
|
-
|
|
1056
|
+
def literal_hash_append(sql, v)
|
|
1057
|
+
literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
|
|
741
1058
|
end
|
|
742
1059
|
|
|
743
1060
|
# SQL fragment for Integer
|
|
@@ -756,9 +1073,11 @@ module Sequel
|
|
|
756
1073
|
# provided and should add that method to Sequel::Dataset, allowing for adapters
|
|
757
1074
|
# to provide customized literalizations.
|
|
758
1075
|
# If a database specific type is allowed, this should be overriden in a subclass.
|
|
759
|
-
def
|
|
760
|
-
if v.respond_to?(:
|
|
761
|
-
v.
|
|
1076
|
+
def literal_other_append(sql, v)
|
|
1077
|
+
if v.respond_to?(:sql_literal_append)
|
|
1078
|
+
v.sql_literal_append(self, sql)
|
|
1079
|
+
elsif v.respond_to?(:sql_literal)
|
|
1080
|
+
sql << v.sql_literal(self)
|
|
762
1081
|
else
|
|
763
1082
|
raise Error, "can't express #{v.inspect} as a SQL literal"
|
|
764
1083
|
end
|
|
@@ -770,8 +1089,8 @@ module Sequel
|
|
|
770
1089
|
end
|
|
771
1090
|
|
|
772
1091
|
# SQL fragment for String. Doubles \ and ' by default.
|
|
773
|
-
def
|
|
774
|
-
|
|
1092
|
+
def literal_string_append(sql, v)
|
|
1093
|
+
sql << APOS << v.gsub(BACKSLASH_RE, QUAD_BACKSLASH).gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
775
1094
|
end
|
|
776
1095
|
|
|
777
1096
|
# Converts a symbol into a column name. This method supports underscore
|
|
@@ -782,10 +1101,14 @@ module Sequel
|
|
|
782
1101
|
# dataset.literal(:abc___a) #=> "abc AS a"
|
|
783
1102
|
# dataset.literal(:items__abc) #=> "items.abc"
|
|
784
1103
|
# dataset.literal(:items__abc___a) #=> "items.abc AS a"
|
|
785
|
-
def
|
|
1104
|
+
def literal_symbol_append(sql, v)
|
|
786
1105
|
c_table, column, c_alias = split_symbol(v)
|
|
787
|
-
|
|
788
|
-
|
|
1106
|
+
if c_table
|
|
1107
|
+
quote_identifier_append(sql, c_table)
|
|
1108
|
+
sql << DOT
|
|
1109
|
+
end
|
|
1110
|
+
quote_identifier_append(sql, column)
|
|
1111
|
+
as_sql_append(sql, c_alias) if c_alias
|
|
789
1112
|
end
|
|
790
1113
|
|
|
791
1114
|
# SQL fragment for Time
|
|
@@ -831,13 +1154,19 @@ module Sequel
|
|
|
831
1154
|
|
|
832
1155
|
# Modify the sql to add the columns selected
|
|
833
1156
|
def select_columns_sql(sql)
|
|
834
|
-
sql <<
|
|
1157
|
+
sql << SPACE
|
|
1158
|
+
column_list_append(sql, @opts[:select])
|
|
835
1159
|
end
|
|
836
1160
|
|
|
837
1161
|
# Modify the sql to add the DISTINCT modifier
|
|
838
1162
|
def select_distinct_sql(sql)
|
|
839
1163
|
if distinct = @opts[:distinct]
|
|
840
|
-
sql <<
|
|
1164
|
+
sql << DISTINCT
|
|
1165
|
+
unless distinct.empty?
|
|
1166
|
+
sql << ON_PAREN
|
|
1167
|
+
expression_list_append(sql, distinct)
|
|
1168
|
+
sql << PAREN_CLOSE
|
|
1169
|
+
end
|
|
841
1170
|
end
|
|
842
1171
|
end
|
|
843
1172
|
|
|
@@ -845,59 +1174,89 @@ module Sequel
|
|
|
845
1174
|
# This uses a subselect for the compound datasets used, because using parantheses doesn't
|
|
846
1175
|
# work on all databases. I consider this an ugly hack, but can't I think of a better default.
|
|
847
1176
|
def select_compounds_sql(sql)
|
|
848
|
-
return unless @opts[:compounds]
|
|
849
|
-
|
|
850
|
-
sql <<
|
|
1177
|
+
return unless c = @opts[:compounds]
|
|
1178
|
+
c.each do |type, dataset, all|
|
|
1179
|
+
sql << SPACE << type.to_s.upcase
|
|
1180
|
+
sql << ALL if all
|
|
1181
|
+
sql << SPACE
|
|
1182
|
+
compound_dataset_sql_append(sql, dataset)
|
|
851
1183
|
end
|
|
852
1184
|
end
|
|
853
1185
|
|
|
854
1186
|
# Modify the sql to add the list of tables to select FROM
|
|
855
1187
|
def select_from_sql(sql)
|
|
856
|
-
|
|
1188
|
+
if f = @opts[:from]
|
|
1189
|
+
sql << FROM
|
|
1190
|
+
source_list_append(sql, f)
|
|
1191
|
+
end
|
|
857
1192
|
end
|
|
858
1193
|
alias delete_from_sql select_from_sql
|
|
859
1194
|
|
|
860
1195
|
# Modify the sql to add the expressions to GROUP BY
|
|
861
1196
|
def select_group_sql(sql)
|
|
862
|
-
|
|
1197
|
+
if group = @opts[:group]
|
|
1198
|
+
sql << GROUP_BY
|
|
1199
|
+
expression_list_append(sql, group)
|
|
1200
|
+
end
|
|
863
1201
|
end
|
|
864
1202
|
|
|
865
1203
|
# Modify the sql to add the filter criteria in the HAVING clause
|
|
866
1204
|
def select_having_sql(sql)
|
|
867
|
-
|
|
1205
|
+
if having = @opts[:having]
|
|
1206
|
+
sql << HAVING
|
|
1207
|
+
literal_append(sql, having)
|
|
1208
|
+
end
|
|
868
1209
|
end
|
|
869
1210
|
|
|
870
1211
|
# Modify the sql to add the list of tables to JOIN to
|
|
871
1212
|
def select_join_sql(sql)
|
|
872
|
-
|
|
1213
|
+
if js = @opts[:join]
|
|
1214
|
+
js.each{|j| literal_append(sql, j)}
|
|
1215
|
+
end
|
|
873
1216
|
end
|
|
874
1217
|
|
|
875
1218
|
# Modify the sql to limit the number of rows returned and offset
|
|
876
1219
|
def select_limit_sql(sql)
|
|
877
|
-
|
|
878
|
-
|
|
1220
|
+
if l = @opts[:limit]
|
|
1221
|
+
sql << LIMIT
|
|
1222
|
+
literal_append(sql, l)
|
|
1223
|
+
end
|
|
1224
|
+
if o = @opts[:offset]
|
|
1225
|
+
sql << OFFSET
|
|
1226
|
+
literal_append(sql, o)
|
|
1227
|
+
end
|
|
879
1228
|
end
|
|
880
1229
|
|
|
881
1230
|
# Modify the sql to support the different types of locking modes.
|
|
882
1231
|
def select_lock_sql(sql)
|
|
883
|
-
case @opts[:lock]
|
|
1232
|
+
case l = @opts[:lock]
|
|
884
1233
|
when :update
|
|
885
1234
|
sql << FOR_UPDATE
|
|
886
1235
|
when String
|
|
887
|
-
sql <<
|
|
1236
|
+
sql << SPACE << l
|
|
888
1237
|
end
|
|
889
1238
|
end
|
|
890
1239
|
|
|
891
1240
|
# Modify the sql to add the expressions to ORDER BY
|
|
892
1241
|
def select_order_sql(sql)
|
|
893
|
-
|
|
1242
|
+
if o = @opts[:order]
|
|
1243
|
+
sql << ORDER_BY
|
|
1244
|
+
expression_list_append(sql, o)
|
|
1245
|
+
end
|
|
894
1246
|
end
|
|
895
1247
|
alias delete_order_sql select_order_sql
|
|
896
1248
|
alias update_order_sql select_order_sql
|
|
897
1249
|
|
|
1250
|
+
def select_select_sql(sql)
|
|
1251
|
+
sql << SELECT
|
|
1252
|
+
end
|
|
1253
|
+
|
|
898
1254
|
# Modify the sql to add the filter criteria in the WHERE clause
|
|
899
1255
|
def select_where_sql(sql)
|
|
900
|
-
|
|
1256
|
+
if w = @opts[:where]
|
|
1257
|
+
sql << WHERE
|
|
1258
|
+
literal_append(sql, w)
|
|
1259
|
+
end
|
|
901
1260
|
end
|
|
902
1261
|
alias delete_where_sql select_where_sql
|
|
903
1262
|
alias update_where_sql select_where_sql
|
|
@@ -906,7 +1265,22 @@ module Sequel
|
|
|
906
1265
|
def select_with_sql(sql)
|
|
907
1266
|
ws = opts[:with]
|
|
908
1267
|
return if !ws || ws.empty?
|
|
909
|
-
sql
|
|
1268
|
+
sql << select_with_sql_base
|
|
1269
|
+
c = false
|
|
1270
|
+
comma = COMMA
|
|
1271
|
+
ws.each do |w|
|
|
1272
|
+
sql << comma if c
|
|
1273
|
+
quote_identifier_append(sql, w[:name])
|
|
1274
|
+
if args = w[:args]
|
|
1275
|
+
sql << PAREN_OPEN
|
|
1276
|
+
argument_list_append(sql, args)
|
|
1277
|
+
sql << PAREN_CLOSE
|
|
1278
|
+
end
|
|
1279
|
+
sql << AS
|
|
1280
|
+
literal_dataset_append(sql, w[:dataset])
|
|
1281
|
+
c ||= true
|
|
1282
|
+
end
|
|
1283
|
+
sql << SPACE
|
|
910
1284
|
end
|
|
911
1285
|
alias delete_with_sql select_with_sql
|
|
912
1286
|
alias insert_with_sql select_with_sql
|
|
@@ -918,9 +1292,15 @@ module Sequel
|
|
|
918
1292
|
end
|
|
919
1293
|
|
|
920
1294
|
# Converts an array of source names into into a comma separated list.
|
|
921
|
-
def
|
|
922
|
-
raise(Error, 'No source specified for query') if
|
|
923
|
-
|
|
1295
|
+
def source_list_append(sql, sources)
|
|
1296
|
+
raise(Error, 'No source specified for query') if sources.nil? || sources == []
|
|
1297
|
+
c = false
|
|
1298
|
+
co = COMMA
|
|
1299
|
+
sources.each do |s|
|
|
1300
|
+
sql << co if c
|
|
1301
|
+
table_ref_append(sql, s)
|
|
1302
|
+
c ||= true
|
|
1303
|
+
end
|
|
924
1304
|
end
|
|
925
1305
|
|
|
926
1306
|
# Splits the symbol into three parts. Each part will
|
|
@@ -929,13 +1309,13 @@ module Sequel
|
|
|
929
1309
|
# For columns, these parts are the table, column, and alias.
|
|
930
1310
|
# For tables, these parts are the schema, table, and alias.
|
|
931
1311
|
def split_symbol(sym)
|
|
932
|
-
s = sym.to_s
|
|
933
|
-
|
|
934
|
-
[
|
|
935
|
-
|
|
936
|
-
[nil,
|
|
937
|
-
|
|
938
|
-
[
|
|
1312
|
+
case s = sym.to_s
|
|
1313
|
+
when COLUMN_REF_RE1
|
|
1314
|
+
[$1, $2, $3]
|
|
1315
|
+
when COLUMN_REF_RE2
|
|
1316
|
+
[nil, $1, $2]
|
|
1317
|
+
when COLUMN_REF_RE3
|
|
1318
|
+
[$1, $2, nil]
|
|
939
1319
|
else
|
|
940
1320
|
[nil, s, nil]
|
|
941
1321
|
end
|
|
@@ -945,18 +1325,35 @@ module Sequel
|
|
|
945
1325
|
# can be a PlaceholderLiteralString in addition to a String,
|
|
946
1326
|
# we literalize nonstrings.
|
|
947
1327
|
def static_sql(sql)
|
|
948
|
-
|
|
1328
|
+
if append_sql = @opts[:append_sql]
|
|
1329
|
+
if sql.is_a?(String)
|
|
1330
|
+
append_sql << sql
|
|
1331
|
+
else
|
|
1332
|
+
literal_append(append_sql, sql)
|
|
1333
|
+
end
|
|
1334
|
+
else
|
|
1335
|
+
if sql.is_a?(String)
|
|
1336
|
+
sql
|
|
1337
|
+
else
|
|
1338
|
+
literal(sql)
|
|
1339
|
+
end
|
|
1340
|
+
end
|
|
949
1341
|
end
|
|
950
1342
|
|
|
951
1343
|
# SQL fragment for a subselect using the given database's SQL.
|
|
952
|
-
def
|
|
953
|
-
ds.sql
|
|
1344
|
+
def subselect_sql_append(sql, ds)
|
|
1345
|
+
ds.clone(:append_sql=>sql).sql
|
|
954
1346
|
end
|
|
955
1347
|
|
|
956
1348
|
# SQL fragment specifying a table name.
|
|
957
|
-
def
|
|
958
|
-
t.is_a?(String)
|
|
1349
|
+
def table_ref_append(sql, t)
|
|
1350
|
+
if t.is_a?(String)
|
|
1351
|
+
quote_identifier_append(sql, t)
|
|
1352
|
+
else
|
|
1353
|
+
literal_append(sql, t)
|
|
1354
|
+
end
|
|
959
1355
|
end
|
|
1356
|
+
alias identifier_append table_ref_append
|
|
960
1357
|
|
|
961
1358
|
# The order of methods to call to build the UPDATE SQL statement
|
|
962
1359
|
def update_clause_methods
|
|
@@ -966,25 +1363,38 @@ module Sequel
|
|
|
966
1363
|
# SQL fragment specifying the tables from with to delete.
|
|
967
1364
|
# Includes join table if modifying joins is allowed.
|
|
968
1365
|
def update_table_sql(sql)
|
|
969
|
-
sql <<
|
|
1366
|
+
sql << SPACE
|
|
1367
|
+
source_list_append(sql, @opts[:from])
|
|
970
1368
|
select_join_sql(sql) if supports_modifying_joins?
|
|
971
1369
|
end
|
|
972
1370
|
|
|
973
1371
|
# The SQL fragment specifying the columns and values to SET.
|
|
974
1372
|
def update_set_sql(sql)
|
|
975
1373
|
values = opts[:values]
|
|
976
|
-
|
|
1374
|
+
sql << SET
|
|
1375
|
+
if values.is_a?(Hash)
|
|
977
1376
|
values = opts[:defaults].merge(values) if opts[:defaults]
|
|
978
1377
|
values = values.merge(opts[:overrides]) if opts[:overrides]
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1378
|
+
c = false
|
|
1379
|
+
eq = EQUAL
|
|
1380
|
+
values.each do |k, v|
|
|
1381
|
+
sql << COMMA if c
|
|
1382
|
+
if k.is_a?(String) && !k.is_a?(LiteralString)
|
|
1383
|
+
quote_identifier_append(sql, k)
|
|
1384
|
+
else
|
|
1385
|
+
literal_append(sql, k)
|
|
1386
|
+
end
|
|
1387
|
+
sql << eq
|
|
1388
|
+
literal_append(sql, v)
|
|
1389
|
+
c ||= true
|
|
1390
|
+
end
|
|
983
1391
|
else
|
|
984
|
-
|
|
985
|
-
values
|
|
1392
|
+
sql << values
|
|
986
1393
|
end
|
|
987
|
-
|
|
1394
|
+
end
|
|
1395
|
+
|
|
1396
|
+
def update_update_sql(sql)
|
|
1397
|
+
sql << UPDATE
|
|
988
1398
|
end
|
|
989
1399
|
end
|
|
990
1400
|
end
|