sequel 3.33.0 → 3.34.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +140 -0
- data/Rakefile +7 -0
- data/bin/sequel +22 -2
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/mass_assignment.rdoc +3 -1
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +23 -3
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/schema_modification.rdoc +18 -2
- data/doc/virtual_rows.rdoc +49 -0
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/ibmdb.rb +9 -4
- data/lib/sequel/adapters/jdbc.rb +9 -4
- data/lib/sequel/adapters/jdbc/h2.rb +8 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
- data/lib/sequel/adapters/mock.rb +24 -3
- data/lib/sequel/adapters/mysql.rb +29 -50
- data/lib/sequel/adapters/mysql2.rb +13 -28
- data/lib/sequel/adapters/oracle.rb +8 -2
- data/lib/sequel/adapters/postgres.rb +115 -20
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -3
- data/lib/sequel/adapters/shared/mysql.rb +59 -11
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +127 -30
- data/lib/sequel/adapters/shared/sqlite.rb +55 -38
- data/lib/sequel/adapters/sqlite.rb +9 -3
- data/lib/sequel/adapters/swift.rb +2 -2
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +10 -0
- data/lib/sequel/ast_transformer.rb +4 -0
- data/lib/sequel/connection_pool.rb +8 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +14 -0
- data/lib/sequel/core.rb +24 -3
- data/lib/sequel/database/connecting.rb +24 -14
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +16 -25
- data/lib/sequel/database/query.rb +20 -2
- data/lib/sequel/database/schema_generator.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +120 -23
- data/lib/sequel/dataset/actions.rb +91 -18
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/prepared_statements.rb +6 -2
- data/lib/sequel/dataset/sql.rb +68 -51
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
- data/lib/sequel/extensions/migration.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +227 -31
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/to_dot.rb +1 -1
- data/lib/sequel/model.rb +11 -2
- data/lib/sequel/model/associations.rb +35 -7
- data/lib/sequel/model/base.rb +159 -36
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +11 -3
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +4 -3
- data/lib/sequel/plugins/prepared_statements.rb +3 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +417 -7
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +12 -15
- data/spec/adapters/mysql_spec.rb +81 -23
- data/spec/adapters/postgres_spec.rb +444 -77
- data/spec/adapters/spec_helper.rb +2 -0
- data/spec/adapters/sqlite_spec.rb +8 -8
- data/spec/core/connection_pool_spec.rb +85 -0
- data/spec/core/database_spec.rb +29 -5
- data/spec/core/dataset_spec.rb +171 -3
- data/spec/core/expression_filters_spec.rb +364 -0
- data/spec/core/mock_adapter_spec.rb +17 -3
- data/spec/core/schema_spec.rb +133 -0
- data/spec/extensions/association_dependencies_spec.rb +13 -13
- data/spec/extensions/caching_spec.rb +26 -3
- data/spec/extensions/class_table_inheritance_spec.rb +2 -2
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +5 -2
- data/spec/extensions/identity_map_spec.rb +17 -0
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/lazy_attributes_spec.rb +2 -2
- data/spec/extensions/list_spec.rb +4 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +6 -2
- data/spec/extensions/nested_attributes_spec.rb +20 -0
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +2 -2
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_spec.rb +4 -0
- data/spec/extensions/pretty_table_spec.rb +6 -0
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +231 -11
- data/spec/extensions/schema_spec.rb +14 -2
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/sharding_spec.rb +6 -6
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +2 -1
- data/spec/extensions/sql_expr_spec.rb +28 -19
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/typecast_on_load_spec.rb +9 -1
- data/spec/integration/associations_test.rb +6 -6
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +89 -26
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +3 -3
- data/spec/integration/plugin_test.rb +85 -22
- data/spec/integration/prepared_statement_test.rb +28 -8
- data/spec/integration/schema_test.rb +78 -7
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +4 -6
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/associations_spec.rb +94 -8
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/hooks_spec.rb +2 -2
- data/spec/model/model_spec.rb +19 -7
- data/spec/model/record_spec.rb +135 -58
- data/spec/model/spec_helper.rb +1 -0
- metadata +35 -7
@@ -11,8 +11,8 @@ module Sequel
|
|
11
11
|
ACTION_METHODS = (<<-METHS).split.map{|x| x.to_sym}
|
12
12
|
<< [] []= all avg count columns columns! delete each
|
13
13
|
empty? fetch_rows first get import insert insert_multiple interval last
|
14
|
-
map max min multi_insert range select_hash select_map select_order_map
|
15
|
-
set single_record single_value sum to_csv to_hash truncate update
|
14
|
+
map max min multi_insert range select_hash select_hash_groups select_map select_order_map
|
15
|
+
set single_record single_value sum to_csv to_hash to_hash_groups truncate update
|
16
16
|
METHS
|
17
17
|
|
18
18
|
# Inserts the given argument into the database. Returns self so it
|
@@ -124,13 +124,13 @@ module Sequel
|
|
124
124
|
# running additional queries inside the provided block. If you are
|
125
125
|
# running queries inside the block, you should use +all+ instead of +each+
|
126
126
|
# for the outer queries, or use a separate thread or shard inside +each+:
|
127
|
-
def each
|
127
|
+
def each
|
128
128
|
if @opts[:graph]
|
129
|
-
graph_each
|
129
|
+
graph_each{|r| yield r}
|
130
130
|
elsif row_proc = @row_proc
|
131
131
|
fetch_rows(select_sql){|r| yield row_proc.call(r)}
|
132
132
|
else
|
133
|
-
fetch_rows(select_sql
|
133
|
+
fetch_rows(select_sql){|r| yield r}
|
134
134
|
end
|
135
135
|
self
|
136
136
|
end
|
@@ -421,7 +421,7 @@ module Sequel
|
|
421
421
|
end
|
422
422
|
|
423
423
|
# Returns a hash with key_column values as keys and value_column values as
|
424
|
-
# values. Similar to to_hash, but only selects the
|
424
|
+
# values. Similar to to_hash, but only selects the columns given.
|
425
425
|
#
|
426
426
|
# DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
|
427
427
|
# # => {1=>'a', 2=>'b', ...}
|
@@ -436,19 +436,28 @@ module Sequel
|
|
436
436
|
# that Sequel can determine. Usually you can do this by calling the #as method
|
437
437
|
# on the expression and providing an alias.
|
438
438
|
def select_hash(key_column, value_column)
|
439
|
-
|
440
|
-
if value_column.is_a?(Array)
|
441
|
-
select(*(key_column + value_column)).to_hash(key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
|
442
|
-
else
|
443
|
-
select(*(key_column + [value_column])).to_hash(key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
|
444
|
-
end
|
445
|
-
elsif value_column.is_a?(Array)
|
446
|
-
select(key_column, *value_column).to_hash(hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
|
447
|
-
else
|
448
|
-
select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
|
449
|
-
end
|
439
|
+
_select_hash(:to_hash, key_column, value_column)
|
450
440
|
end
|
451
441
|
|
442
|
+
# Returns a hash with key_column values as keys and an array of value_column values.
|
443
|
+
# Similar to to_hash_groups, but only selects the columns given.
|
444
|
+
#
|
445
|
+
# DB[:table].select_hash(:name, :id) # SELECT id, name FROM table
|
446
|
+
# # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
|
447
|
+
#
|
448
|
+
# You can also provide an array of column names for either the key_column,
|
449
|
+
# the value column, or both:
|
450
|
+
#
|
451
|
+
# DB[:table].select_hash([:first, :middle], [:last, :id]) # SELECT * FROM table
|
452
|
+
# # {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
|
453
|
+
#
|
454
|
+
# When using this method, you must be sure that each expression has an alias
|
455
|
+
# that Sequel can determine. Usually you can do this by calling the #as method
|
456
|
+
# on the expression and providing an alias.
|
457
|
+
def select_hash_groups(key_column, value_column)
|
458
|
+
_select_hash(:to_hash_groups, key_column, value_column)
|
459
|
+
end
|
460
|
+
|
452
461
|
# Selects the column given (either as an argument or as a block), and
|
453
462
|
# returns an array of all values of that column in the dataset. If you
|
454
463
|
# give a block argument that returns an array with multiple entries,
|
@@ -472,7 +481,6 @@ module Sequel
|
|
472
481
|
def select_map(column=nil, &block)
|
473
482
|
_select_map(column, false, &block)
|
474
483
|
end
|
475
|
-
|
476
484
|
|
477
485
|
# The same as select_map, but in addition orders the array by the column.
|
478
486
|
#
|
@@ -591,6 +599,49 @@ module Sequel
|
|
591
599
|
h
|
592
600
|
end
|
593
601
|
|
602
|
+
# Returns a hash with one column used as key and the values being an
|
603
|
+
# array of column values. If the value_column is not given or nil, uses
|
604
|
+
# the entire hash as the value.
|
605
|
+
#
|
606
|
+
# DB[:table].to_hash(:name, :id) # SELECT * FROM table
|
607
|
+
# # {'Jim'=>[1, 4, 16, ...], 'Bob'=>[2], ...}
|
608
|
+
#
|
609
|
+
# DB[:table].to_hash(:name) # SELECT * FROM table
|
610
|
+
# # {'Jim'=>[{:id=>1, :name=>'Jim'}, {:id=>4, :name=>'Jim'}, ...], 'Bob'=>[{:id=>2, :name=>'Bob'}], ...}
|
611
|
+
#
|
612
|
+
# You can also provide an array of column names for either the key_column,
|
613
|
+
# the value column, or both:
|
614
|
+
#
|
615
|
+
# DB[:table].to_hash([:first, :middle], [:last, :id]) # SELECT * FROM table
|
616
|
+
# # {['Jim', 'Bob']=>[['Smith', 1], ['Jackson', 4], ...], ...}
|
617
|
+
#
|
618
|
+
# DB[:table].to_hash([:first, :middle]) # SELECT * FROM table
|
619
|
+
# # {['Jim', 'Bob']=>[{:id=>1, :first=>'Jim', :middle=>'Bob', :last=>'Smith'}, ...], ...}
|
620
|
+
def to_hash_groups(key_column, value_column = nil)
|
621
|
+
h = {}
|
622
|
+
if value_column
|
623
|
+
return naked.to_hash_groups(key_column, value_column) if row_proc
|
624
|
+
if value_column.is_a?(Array)
|
625
|
+
if key_column.is_a?(Array)
|
626
|
+
each{|r| (h[r.values_at(*key_column)] ||= []) << r.values_at(*value_column)}
|
627
|
+
else
|
628
|
+
each{|r| (h[r[key_column]] ||= []) << r.values_at(*value_column)}
|
629
|
+
end
|
630
|
+
else
|
631
|
+
if key_column.is_a?(Array)
|
632
|
+
each{|r| (h[r.values_at(*key_column)] ||= []) << r[value_column]}
|
633
|
+
else
|
634
|
+
each{|r| (h[r[key_column]] ||= []) << r[value_column]}
|
635
|
+
end
|
636
|
+
end
|
637
|
+
elsif key_column.is_a?(Array)
|
638
|
+
each{|r| (h[r.values_at(*key_column)] ||= []) << r}
|
639
|
+
else
|
640
|
+
each{|r| (h[r[key_column]] ||= []) << r}
|
641
|
+
end
|
642
|
+
h
|
643
|
+
end
|
644
|
+
|
594
645
|
# Truncates the dataset. Returns nil.
|
595
646
|
#
|
596
647
|
# DB[:table].truncate # TRUNCATE table
|
@@ -618,6 +669,13 @@ module Sequel
|
|
618
669
|
end
|
619
670
|
end
|
620
671
|
|
672
|
+
# Execute the given SQL and return the number of rows deleted. This exists
|
673
|
+
# solely as an optimization, replacing with_sql(sql).delete. It's significantly
|
674
|
+
# faster as it does not require cloning the current dataset.
|
675
|
+
def with_sql_delete(sql)
|
676
|
+
execute_dui(sql)
|
677
|
+
end
|
678
|
+
|
621
679
|
protected
|
622
680
|
|
623
681
|
# Internals of #import. If primary key values are requested, use
|
@@ -645,6 +703,21 @@ module Sequel
|
|
645
703
|
|
646
704
|
private
|
647
705
|
|
706
|
+
# Internals of +select_hash+ and +select_hash_groups+
|
707
|
+
def _select_hash(meth, key_column, value_column)
|
708
|
+
if key_column.is_a?(Array)
|
709
|
+
if value_column.is_a?(Array)
|
710
|
+
select(*(key_column + value_column)).send(meth, key_column.map{|c| hash_key_symbol(c)}, value_column.map{|c| hash_key_symbol(c)})
|
711
|
+
else
|
712
|
+
select(*(key_column + [value_column])).send(meth, key_column.map{|c| hash_key_symbol(c)}, hash_key_symbol(value_column))
|
713
|
+
end
|
714
|
+
elsif value_column.is_a?(Array)
|
715
|
+
select(key_column, *value_column).send(meth, hash_key_symbol(key_column), value_column.map{|c| hash_key_symbol(c)})
|
716
|
+
else
|
717
|
+
select(key_column, value_column).send(meth, hash_key_symbol(key_column), hash_key_symbol(value_column))
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
648
721
|
# Internals of +select_map+ and +select_order_map+
|
649
722
|
def _select_map(column, order, &block)
|
650
723
|
ds = naked.ungraphed
|
@@ -165,5 +165,10 @@ module Sequel
|
|
165
165
|
def uses_returning?(type)
|
166
166
|
opts[:returning] && !@opts[:sql] && supports_returning?(type)
|
167
167
|
end
|
168
|
+
|
169
|
+
# Whether the dataset uses WITH ROLLUP/CUBE instead of ROLLUP()/CUBE().
|
170
|
+
def uses_with_rollup?
|
171
|
+
false
|
172
|
+
end
|
168
173
|
end
|
169
174
|
end
|
@@ -17,7 +17,7 @@ module Sequel
|
|
17
17
|
|
18
18
|
# The bind arguments to use for running this prepared statement
|
19
19
|
attr_accessor :bind_arguments
|
20
|
-
|
20
|
+
|
21
21
|
# Set the bind arguments based on the hash and call super.
|
22
22
|
def call(bind_vars={}, &block)
|
23
23
|
ds = bind(bind_vars)
|
@@ -46,6 +46,10 @@ module Sequel
|
|
46
46
|
module PreparedStatementMethods
|
47
47
|
PLACEHOLDER_RE = /\A\$(.*)\z/
|
48
48
|
|
49
|
+
# Whether to log the full SQL query. By default, just the prepared statement
|
50
|
+
# name is generally logged on adapters that support native prepared statements.
|
51
|
+
attr_accessor :log_sql
|
52
|
+
|
49
53
|
# The type of prepared statement, should be one of :select, :first,
|
50
54
|
# :insert, :update, or :delete
|
51
55
|
attr_accessor :prepared_type
|
@@ -139,7 +143,7 @@ module Sequel
|
|
139
143
|
delete
|
140
144
|
when Array
|
141
145
|
case @prepared_type.at(0)
|
142
|
-
when :map, :to_hash
|
146
|
+
when :map, :to_hash, :to_hash_groups
|
143
147
|
send(*@prepared_type, &block)
|
144
148
|
end
|
145
149
|
else
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -20,7 +20,7 @@ module Sequel
|
|
20
20
|
# DB.select(1).where(DB[:items].exists)
|
21
21
|
# # SELECT 1 WHERE (EXISTS (SELECT * FROM items))
|
22
22
|
def exists
|
23
|
-
SQL::PlaceholderLiteralString.new(
|
23
|
+
SQL::PlaceholderLiteralString.new(EXISTS, [self], true)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Returns an INSERT SQL query string. See +insert+.
|
@@ -63,7 +63,7 @@ module Sequel
|
|
63
63
|
|
64
64
|
if values.is_a?(Array) && values.empty? && !insert_supports_empty_values?
|
65
65
|
columns = [columns().last]
|
66
|
-
values = [
|
66
|
+
values = [DEFAULT]
|
67
67
|
end
|
68
68
|
clone(:columns=>columns, :values=>values)._insert_sql
|
69
69
|
end
|
@@ -182,6 +182,7 @@ module Sequel
|
|
182
182
|
clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
|
183
183
|
end
|
184
184
|
|
185
|
+
WILDCARD = LiteralString.new('*').freeze
|
185
186
|
ALL = ' ALL'.freeze
|
186
187
|
AND_SEPARATOR = " AND ".freeze
|
187
188
|
APOS = "'".freeze
|
@@ -200,7 +201,6 @@ module Sequel
|
|
200
201
|
CASE_THEN = " THEN ".freeze
|
201
202
|
CASE_WHEN = " WHEN ".freeze
|
202
203
|
CAST_OPEN = 'CAST('.freeze
|
203
|
-
COLUMN_ALL = '.*'.freeze
|
204
204
|
COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
|
205
205
|
COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
|
206
206
|
COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
|
@@ -209,8 +209,9 @@ module Sequel
|
|
209
209
|
CONDITION_FALSE = '(1 = 0)'.freeze
|
210
210
|
CONDITION_TRUE = '(1 = 1)'.freeze
|
211
211
|
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
|
212
|
-
COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count,
|
212
|
+
COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, WILDCARD).as(:count)
|
213
213
|
DATASET_ALIAS_BASE_NAME = 't'.freeze
|
214
|
+
DEFAULT = LiteralString.new('DEFAULT').freeze
|
214
215
|
DEFAULT_VALUES = " DEFAULT VALUES".freeze
|
215
216
|
DELETE = 'DELETE'.freeze
|
216
217
|
DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'delete from where')
|
@@ -221,6 +222,7 @@ module Sequel
|
|
221
222
|
DOUBLE_QUOTE = '""'.freeze
|
222
223
|
EQUAL = ' = '.freeze
|
223
224
|
EXTRACT = 'extract('.freeze
|
225
|
+
EXISTS = ['EXISTS '.freeze].freeze
|
224
226
|
FOR_UPDATE = ' FOR UPDATE'.freeze
|
225
227
|
FORMAT_DATE = "'%Y-%m-%d'".freeze
|
226
228
|
FORMAT_DATE_STANDARD = "DATE '%Y-%m-%d'".freeze
|
@@ -267,6 +269,7 @@ module Sequel
|
|
267
269
|
SET = ' SET '.freeze
|
268
270
|
SPACE = ' '.freeze
|
269
271
|
SQL_WITH = "WITH ".freeze
|
272
|
+
SPACE_WITH = " WITH ".freeze
|
270
273
|
TILDE = '~'.freeze
|
271
274
|
TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S%N%z'".freeze
|
272
275
|
STANDARD_TIMESTAMP_FORMAT = "TIMESTAMP #{TIMESTAMP_FORMAT}".freeze
|
@@ -276,8 +279,9 @@ module Sequel
|
|
276
279
|
UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'update table set where')
|
277
280
|
USING = ' USING ('.freeze
|
278
281
|
VALUES = " VALUES ".freeze
|
282
|
+
V187 = '1.8.7'.freeze
|
283
|
+
V190 = '1.9.0'.freeze
|
279
284
|
WHERE = " WHERE ".freeze
|
280
|
-
WILDCARD = LiteralString.new('*').freeze
|
281
285
|
|
282
286
|
PUBLIC_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
|
283
287
|
literal
|
@@ -392,10 +396,10 @@ module Sequel
|
|
392
396
|
|
393
397
|
# SQL fragment for specifying all columns in a given table
|
394
398
|
def column_all_sql_append(sql, ca)
|
395
|
-
|
396
|
-
sql << COLUMN_ALL
|
399
|
+
qualified_identifier_sql_append(sql, ca.table, WILDCARD)
|
397
400
|
end
|
398
401
|
|
402
|
+
# SQL fragment for the complex expression.
|
399
403
|
def complex_expression_sql_append(sql, op, args)
|
400
404
|
case op
|
401
405
|
when *IS_OPERATORS
|
@@ -509,7 +513,7 @@ module Sequel
|
|
509
513
|
table_alias = jc.table_alias
|
510
514
|
table_alias = nil if table == table_alias
|
511
515
|
sql << SPACE << join_type_sql(jc.join_type) << SPACE
|
512
|
-
|
516
|
+
identifier_append(sql, table)
|
513
517
|
as_sql_append(sql, table_alias) if table_alias
|
514
518
|
end
|
515
519
|
|
@@ -550,11 +554,11 @@ module Sequel
|
|
550
554
|
# SQL fragment for a literal string with placeholders
|
551
555
|
def placeholder_literal_string_sql_append(sql, pls)
|
552
556
|
args = pls.args
|
557
|
+
str = pls.str
|
553
558
|
sql << PAREN_OPEN if pls.parens
|
554
559
|
if args.is_a?(Hash)
|
555
560
|
re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
|
556
|
-
if RUBY_VERSION >=
|
557
|
-
str = pls.str
|
561
|
+
if RUBY_VERSION >= V187
|
558
562
|
loop do
|
559
563
|
previous, q, str = str.partition(re)
|
560
564
|
sql << previous
|
@@ -562,12 +566,17 @@ module Sequel
|
|
562
566
|
break if str.empty?
|
563
567
|
end
|
564
568
|
else
|
565
|
-
sql <<
|
569
|
+
sql << str.gsub(re){literal(args[$1.to_sym])}
|
570
|
+
end
|
571
|
+
elsif str.is_a?(Array)
|
572
|
+
len = args.length
|
573
|
+
str.each_with_index do |s, i|
|
574
|
+
sql << s
|
575
|
+
literal_append(sql, args[i]) unless i == len
|
566
576
|
end
|
567
577
|
else
|
568
578
|
i = -1
|
569
|
-
if RUBY_VERSION >=
|
570
|
-
str = pls.str
|
579
|
+
if RUBY_VERSION >= V187
|
571
580
|
loop do
|
572
581
|
previous, q, str = str.partition(QUESTION_MARK)
|
573
582
|
sql << previous
|
@@ -575,7 +584,7 @@ module Sequel
|
|
575
584
|
break if str.empty?
|
576
585
|
end
|
577
586
|
else
|
578
|
-
sql <<
|
587
|
+
sql << str.gsub(QUESTION_MARK_RE){literal(args.at(i+=1))}
|
579
588
|
end
|
580
589
|
end
|
581
590
|
sql << PAREN_CLOSE if pls.parens
|
@@ -583,20 +592,12 @@ module Sequel
|
|
583
592
|
|
584
593
|
# SQL fragment for the qualifed identifier, specifying
|
585
594
|
# a table and a column (or schema and table).
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
else
|
591
|
-
quote_identifier_append(sql, t)
|
592
|
-
end
|
595
|
+
# If 3 arguments are given, the 2nd should be the table/qualifier and the third should be
|
596
|
+
# column/qualified. If 2 arguments are given, the 2nd should be an SQL::QualifiedIdentifier.
|
597
|
+
def qualified_identifier_sql_append(sql, table, column=(c = table.column; table = table.table; c))
|
598
|
+
identifier_append(sql, table)
|
593
599
|
sql << DOT
|
594
|
-
|
595
|
-
when Symbol, SQL::QualifiedIdentifier, SQL::Identifier
|
596
|
-
literal_append(sql, c)
|
597
|
-
else
|
598
|
-
quote_identifier_append(sql, c)
|
599
|
-
end
|
600
|
+
identifier_append(sql, column)
|
600
601
|
end
|
601
602
|
|
602
603
|
# Adds quoting to identifiers (columns and tables). If identifiers are not
|
@@ -802,7 +803,7 @@ module Sequel
|
|
802
803
|
|
803
804
|
# Prepare an SQL statement by calling all clause methods for the given statement type.
|
804
805
|
def clause_sql(type)
|
805
|
-
sql = @opts[:append_sql] ||
|
806
|
+
sql = @opts[:append_sql] || sql_string_origin
|
806
807
|
send("#{type}_clause_methods").each{|x| send(x, sql)}
|
807
808
|
sql
|
808
809
|
end
|
@@ -869,7 +870,7 @@ module Sequel
|
|
869
870
|
c ||= true
|
870
871
|
end
|
871
872
|
end
|
872
|
-
|
873
|
+
|
873
874
|
def empty_array_value(op, cols)
|
874
875
|
if Sequel.empty_array_handle_nulls
|
875
876
|
c = Array(cols)
|
@@ -887,7 +888,7 @@ module Sequel
|
|
887
888
|
v2 = db.from_application_timestamp(v)
|
888
889
|
fmt = default_timestamp_format.gsub(FORMAT_TIMESTAMP_RE) do |m|
|
889
890
|
if m == FORMAT_USEC
|
890
|
-
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION <
|
891
|
+
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < V190 ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
|
891
892
|
else
|
892
893
|
if supports_timestamp_timezones?
|
893
894
|
# Would like to just use %z format, but it doesn't appear to work on Windows
|
@@ -911,6 +912,24 @@ module Sequel
|
|
911
912
|
sprintf(FORMAT_TIMESTAMP_USEC, usec)
|
912
913
|
end
|
913
914
|
|
915
|
+
# Append the value, but special case regular (non-literal, non-blob) strings
|
916
|
+
# so that they are considered as identifiers and not SQL strings.
|
917
|
+
def identifier_append(sql, v)
|
918
|
+
if v.is_a?(String)
|
919
|
+
case v
|
920
|
+
when LiteralString
|
921
|
+
sql << v
|
922
|
+
when SQL::Blob
|
923
|
+
literal_append(sql, v)
|
924
|
+
else
|
925
|
+
quote_identifier_append(sql, v)
|
926
|
+
end
|
927
|
+
else
|
928
|
+
literal_append(sql, v)
|
929
|
+
end
|
930
|
+
end
|
931
|
+
alias table_ref_append identifier_append
|
932
|
+
|
914
933
|
# Modify the identifier returned from the database based on the
|
915
934
|
# identifier_output_method.
|
916
935
|
def input_identifier(v)
|
@@ -937,11 +956,7 @@ module Sequel
|
|
937
956
|
co = COMMA
|
938
957
|
columns.each do |col|
|
939
958
|
sql << co if c
|
940
|
-
|
941
|
-
quote_identifier_append(sql, col)
|
942
|
-
else
|
943
|
-
literal_append(sql, col)
|
944
|
-
end
|
959
|
+
identifier_append(sql, col)
|
945
960
|
c ||= true
|
946
961
|
end
|
947
962
|
sql << PAREN_CLOSE
|
@@ -1058,7 +1073,7 @@ module Sequel
|
|
1058
1073
|
def literal_integer(v)
|
1059
1074
|
v.to_s
|
1060
1075
|
end
|
1061
|
-
|
1076
|
+
|
1062
1077
|
# SQL fragment for nil
|
1063
1078
|
def literal_nil
|
1064
1079
|
NULL
|
@@ -1194,11 +1209,17 @@ module Sequel
|
|
1194
1209
|
if group = @opts[:group]
|
1195
1210
|
sql << GROUP_BY
|
1196
1211
|
if go = @opts[:group_options]
|
1197
|
-
|
1198
|
-
|
1212
|
+
if uses_with_rollup?
|
1213
|
+
expression_list_append(sql, group)
|
1214
|
+
sql << SPACE_WITH << go.to_s.upcase
|
1215
|
+
else
|
1216
|
+
sql << go.to_s.upcase << PAREN_OPEN
|
1217
|
+
expression_list_append(sql, group)
|
1218
|
+
sql << PAREN_CLOSE
|
1219
|
+
end
|
1220
|
+
else
|
1221
|
+
expression_list_append(sql, group)
|
1199
1222
|
end
|
1200
|
-
expression_list_append(sql, group)
|
1201
|
-
sql << PAREN_CLOSE if go
|
1202
1223
|
end
|
1203
1224
|
end
|
1204
1225
|
|
@@ -1300,7 +1321,7 @@ module Sequel
|
|
1300
1321
|
co = COMMA
|
1301
1322
|
sources.each do |s|
|
1302
1323
|
sql << co if c
|
1303
|
-
|
1324
|
+
identifier_append(sql, s)
|
1304
1325
|
c ||= true
|
1305
1326
|
end
|
1306
1327
|
end
|
@@ -1323,6 +1344,12 @@ module Sequel
|
|
1323
1344
|
end
|
1324
1345
|
end
|
1325
1346
|
|
1347
|
+
# The string that is appended to to create the SQL query, the empty
|
1348
|
+
# string by default
|
1349
|
+
def sql_string_origin
|
1350
|
+
''
|
1351
|
+
end
|
1352
|
+
|
1326
1353
|
# SQL to use if this dataset uses static SQL. Since static SQL
|
1327
1354
|
# can be a PlaceholderLiteralString in addition to a String,
|
1328
1355
|
# we literalize nonstrings.
|
@@ -1347,16 +1374,6 @@ module Sequel
|
|
1347
1374
|
ds.clone(:append_sql=>sql).sql
|
1348
1375
|
end
|
1349
1376
|
|
1350
|
-
# SQL fragment specifying a table name.
|
1351
|
-
def table_ref_append(sql, t)
|
1352
|
-
if t.is_a?(String)
|
1353
|
-
quote_identifier_append(sql, t)
|
1354
|
-
else
|
1355
|
-
literal_append(sql, t)
|
1356
|
-
end
|
1357
|
-
end
|
1358
|
-
alias identifier_append table_ref_append
|
1359
|
-
|
1360
1377
|
# The order of methods to call to build the UPDATE SQL statement
|
1361
1378
|
def update_clause_methods
|
1362
1379
|
UPDATE_CLAUSE_METHODS
|