sequel 4.10.0 → 4.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +58 -0
- data/doc/association_basics.rdoc +1 -1
- data/doc/cheat_sheet.rdoc +0 -1
- data/doc/core_extensions.rdoc +2 -2
- data/doc/dataset_filtering.rdoc +5 -5
- data/doc/model_hooks.rdoc +9 -0
- data/doc/object_model.rdoc +7 -13
- data/doc/opening_databases.rdoc +3 -1
- data/doc/querying.rdoc +8 -8
- data/doc/release_notes/4.11.0.txt +147 -0
- data/doc/sql.rdoc +11 -7
- data/doc/virtual_rows.rdoc +4 -5
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
- data/lib/sequel/adapters/mock.rb +14 -2
- data/lib/sequel/adapters/shared/access.rb +6 -9
- data/lib/sequel/adapters/shared/cubrid.rb +5 -0
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/firebird.rb +5 -0
- data/lib/sequel/adapters/shared/mssql.rb +23 -16
- data/lib/sequel/adapters/shared/mysql.rb +12 -2
- data/lib/sequel/adapters/shared/oracle.rb +31 -15
- data/lib/sequel/adapters/shared/postgres.rb +28 -4
- data/lib/sequel/adapters/shared/sqlanywhere.rb +5 -0
- data/lib/sequel/adapters/shared/sqlite.rb +12 -1
- data/lib/sequel/ast_transformer.rb +9 -7
- data/lib/sequel/connection_pool.rb +10 -4
- data/lib/sequel/database/features.rb +15 -0
- data/lib/sequel/database/schema_generator.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +21 -3
- data/lib/sequel/database/transactions.rb +8 -4
- data/lib/sequel/dataset/actions.rb +13 -7
- data/lib/sequel/dataset/features.rb +7 -0
- data/lib/sequel/dataset/query.rb +28 -11
- data/lib/sequel/dataset/sql.rb +90 -14
- data/lib/sequel/extensions/constraint_validations.rb +2 -2
- data/lib/sequel/extensions/date_arithmetic.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +12 -6
- data/lib/sequel/extensions/pg_array_ops.rb +11 -2
- data/lib/sequel/extensions/pg_json.rb +130 -23
- data/lib/sequel/extensions/pg_json_ops.rb +196 -28
- data/lib/sequel/extensions/to_dot.rb +5 -7
- data/lib/sequel/model/associations.rb +0 -50
- data/lib/sequel/plugins/class_table_inheritance.rb +49 -21
- data/lib/sequel/plugins/many_through_many.rb +10 -11
- data/lib/sequel/plugins/serialization.rb +4 -1
- data/lib/sequel/plugins/sharding.rb +0 -9
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -2
- data/lib/sequel/plugins/timestamps.rb +2 -2
- data/lib/sequel/sql.rb +166 -44
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +199 -133
- data/spec/core/connection_pool_spec.rb +6 -0
- data/spec/core/database_spec.rb +12 -0
- data/spec/core/dataset_spec.rb +58 -3
- data/spec/core/expression_filters_spec.rb +67 -5
- data/spec/core/mock_adapter_spec.rb +8 -4
- data/spec/core/schema_spec.rb +7 -0
- data/spec/core_extensions_spec.rb +14 -0
- data/spec/extensions/class_table_inheritance_spec.rb +23 -3
- data/spec/extensions/core_refinements_spec.rb +14 -0
- data/spec/extensions/eval_inspect_spec.rb +8 -4
- data/spec/extensions/pg_array_ops_spec.rb +6 -0
- data/spec/extensions/pg_json_ops_spec.rb +99 -0
- data/spec/extensions/pg_json_spec.rb +104 -4
- data/spec/extensions/serialization_spec.rb +19 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -3
- data/spec/extensions/timestamps_spec.rb +10 -0
- data/spec/extensions/to_dot_spec.rb +8 -4
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +9 -0
- data/spec/integration/schema_test.rb +27 -0
- metadata +4 -2
@@ -613,6 +613,11 @@ module Sequel
|
|
613
613
|
db.sqlite_version >= 30803
|
614
614
|
end
|
615
615
|
|
616
|
+
# SQLite does not support table aliases with column aliases
|
617
|
+
def supports_derived_column_lists?
|
618
|
+
false
|
619
|
+
end
|
620
|
+
|
616
621
|
# SQLite does not support INTERSECT ALL or EXCEPT ALL
|
617
622
|
def supports_intersect_except_all?
|
618
623
|
false
|
@@ -643,7 +648,8 @@ module Sequel
|
|
643
648
|
private
|
644
649
|
|
645
650
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
646
|
-
def as_sql_append(sql, aliaz)
|
651
|
+
def as_sql_append(sql, aliaz, column_aliases=nil)
|
652
|
+
raise Error, "sqlite does not support derived column lists" if column_aliases
|
647
653
|
aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
|
648
654
|
sql << AS
|
649
655
|
literal_append(sql, aliaz.to_s)
|
@@ -666,6 +672,11 @@ module Sequel
|
|
666
672
|
end
|
667
673
|
end
|
668
674
|
|
675
|
+
# SQLite supports a maximum of 500 rows in a VALUES clause.
|
676
|
+
def default_import_slice
|
677
|
+
500
|
678
|
+
end
|
679
|
+
|
669
680
|
# SQL fragment specifying a list of identifiers
|
670
681
|
def identifier_list(columns)
|
671
682
|
columns.map{|i| quote_identifier(i)}.join(COMMA)
|
@@ -33,7 +33,7 @@ module Sequel
|
|
33
33
|
when SQL::OrderedExpression
|
34
34
|
SQL::OrderedExpression.new(v(o.expression), o.descending, :nulls=>o.nulls)
|
35
35
|
when SQL::AliasedExpression
|
36
|
-
SQL::AliasedExpression.new(v(o.expression), o.alias)
|
36
|
+
SQL::AliasedExpression.new(v(o.expression), o.alias, o.columns)
|
37
37
|
when SQL::CaseExpression
|
38
38
|
args = [v(o.conditions), v(o.default)]
|
39
39
|
args << v(o.expression) if o.expression?
|
@@ -41,11 +41,13 @@ module Sequel
|
|
41
41
|
when SQL::Cast
|
42
42
|
SQL::Cast.new(v(o.expr), o.type)
|
43
43
|
when SQL::Function
|
44
|
-
|
44
|
+
h = {}
|
45
|
+
o.opts.each do |k, val|
|
46
|
+
h[k] = v(val)
|
47
|
+
end
|
48
|
+
SQL::Function.new!(o.name, v(o.args), h)
|
45
49
|
when SQL::Subscript
|
46
50
|
SQL::Subscript.new(v(o.f), v(o.sub))
|
47
|
-
when SQL::WindowFunction
|
48
|
-
SQL::WindowFunction.new(v(o.function), v(o.window))
|
49
51
|
when SQL::Window
|
50
52
|
opts = o.opts.dup
|
51
53
|
opts[:partition] = v(opts[:partition]) if opts[:partition]
|
@@ -61,11 +63,11 @@ module Sequel
|
|
61
63
|
end
|
62
64
|
SQL::PlaceholderLiteralString.new(o.str, args, o.parens)
|
63
65
|
when SQL::JoinOnClause
|
64
|
-
SQL::JoinOnClause.new(v(o.on), o.join_type, v(o.
|
66
|
+
SQL::JoinOnClause.new(v(o.on), o.join_type, v(o.table_expr))
|
65
67
|
when SQL::JoinUsingClause
|
66
|
-
SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.
|
68
|
+
SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table_expr))
|
67
69
|
when SQL::JoinClause
|
68
|
-
SQL::JoinClause.new(o.join_type, v(o.
|
70
|
+
SQL::JoinClause.new(o.join_type, v(o.table_expr))
|
69
71
|
when SQL::DelayedEvaluation
|
70
72
|
SQL::DelayedEvaluation.new(lambda{v(o.callable.call)})
|
71
73
|
when SQL::Wrapper
|
@@ -69,9 +69,9 @@ class Sequel::ConnectionPool
|
|
69
69
|
# with a single symbol (specifying the server/shard to use) every time a new
|
70
70
|
# connection is needed. The following options are respected for all connection
|
71
71
|
# pools:
|
72
|
-
# :after_connect ::
|
73
|
-
# connection object
|
74
|
-
# connections.
|
72
|
+
# :after_connect :: A callable object called after each new connection is made, with the
|
73
|
+
# connection object (and server argument if the callable accepts 2 arguments),
|
74
|
+
# useful for customizations that you want to apply to all connections.
|
75
75
|
def initialize(db, opts=OPTS)
|
76
76
|
@db = db
|
77
77
|
@after_connect = opts[:after_connect]
|
@@ -94,7 +94,13 @@ class Sequel::ConnectionPool
|
|
94
94
|
def make_new(server)
|
95
95
|
begin
|
96
96
|
conn = @db.connect(server)
|
97
|
-
|
97
|
+
if ac = @after_connect
|
98
|
+
if ac.arity == 2
|
99
|
+
ac.call(conn, server)
|
100
|
+
else
|
101
|
+
ac.call(conn)
|
102
|
+
end
|
103
|
+
end
|
98
104
|
rescue Exception=>exception
|
99
105
|
raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
|
100
106
|
end
|
@@ -94,6 +94,16 @@ module Sequel
|
|
94
94
|
false
|
95
95
|
end
|
96
96
|
|
97
|
+
# Whether CREATE VIEW ... WITH CHECK OPTION is supported, false by default.
|
98
|
+
def supports_views_with_check_option?
|
99
|
+
!!view_with_check_option_support
|
100
|
+
end
|
101
|
+
|
102
|
+
# Whether CREATE VIEW ... WITH LOCAL CHECK OPTION is supported, false by default.
|
103
|
+
def supports_views_with_local_check_option?
|
104
|
+
view_with_check_option_support == :local
|
105
|
+
end
|
106
|
+
|
97
107
|
private
|
98
108
|
|
99
109
|
# Whether the database supports combining multiple alter table
|
@@ -115,5 +125,10 @@ module Sequel
|
|
115
125
|
def supports_named_column_constraints?
|
116
126
|
true
|
117
127
|
end
|
128
|
+
|
129
|
+
# Don't advertise support for WITH CHECK OPTION by default.
|
130
|
+
def view_with_check_option_support
|
131
|
+
nil
|
132
|
+
end
|
118
133
|
end
|
119
134
|
end
|
@@ -322,9 +322,9 @@ module Sequel
|
|
322
322
|
# See CreateTableGenerator#constraint.
|
323
323
|
#
|
324
324
|
# add_constraint(:valid_name, Sequel.like(:name, 'A%'))
|
325
|
-
# # ADD CONSTRAINT valid_name CHECK (name LIKE 'A%')
|
325
|
+
# # ADD CONSTRAINT valid_name CHECK (name LIKE 'A%' ESCAPE '\')
|
326
326
|
# add_constraint({:name=>:valid_name, :deferrable=>true}, :num=>1..5)
|
327
|
-
# # CONSTRAINT valid_name CHECK (name LIKE 'A%') DEFERRABLE INITIALLY DEFERRED
|
327
|
+
# # ADD CONSTRAINT valid_name CHECK (name LIKE 'A%' ESCAPE '\') DEFERRABLE INITIALLY DEFERRED
|
328
328
|
def add_constraint(name, *args, &block)
|
329
329
|
opts = name.is_a?(Hash) ? name : {:name=>name}
|
330
330
|
@operations << opts.merge(:op=>:add_constraint, :type=>:check, :check=>block || args)
|
@@ -241,16 +241,30 @@ module Sequel
|
|
241
241
|
# Creates a view based on a dataset or an SQL string:
|
242
242
|
#
|
243
243
|
# DB.create_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
|
244
|
-
#
|
244
|
+
# # CREATE VIEW cheap_items AS
|
245
|
+
# # SELECT * FROM items WHERE price < 100
|
246
|
+
#
|
247
|
+
# DB.create_view(:ruby_items, DB[:items].where(:category => 'ruby'))
|
248
|
+
# # CREATE VIEW ruby_items AS
|
249
|
+
# # SELECT * FROM items WHERE (category = 'ruby')
|
250
|
+
#
|
251
|
+
# DB.create_view(:checked_items, DB[:items].where(:foo), :check=>true)
|
252
|
+
# # CREATE VIEW checked_items AS
|
253
|
+
# # SELECT * FROM items WHERE foo
|
254
|
+
# # WITH CHECK OPTION
|
245
255
|
#
|
246
256
|
# Options:
|
247
257
|
# :columns :: The column names to use for the view. If not given,
|
248
258
|
# automatically determined based on the input dataset.
|
259
|
+
# :check :: Adds a WITH CHECK OPTION clause, so that attempting to modify
|
260
|
+
# rows in the underlying table that would not be returned by the
|
261
|
+
# view is not allowed. This can be set to :local to use WITH
|
262
|
+
# LOCAL CHECK OPTION.
|
249
263
|
#
|
250
264
|
# PostgreSQL/SQLite specific option:
|
251
265
|
# :temp :: Create a temporary view, automatically dropped on disconnect.
|
252
266
|
#
|
253
|
-
# PostgreSQL specific
|
267
|
+
# PostgreSQL specific options:
|
254
268
|
# :materialized :: Creates a materialized view, similar to a regular view,
|
255
269
|
# but backed by a physical table.
|
256
270
|
# :recursive :: Creates a recursive view. As columns must be specified for
|
@@ -683,7 +697,11 @@ module Sequel
|
|
683
697
|
# DDL statement for creating a view.
|
684
698
|
def create_view_sql(name, source, options)
|
685
699
|
source = source.sql if source.is_a?(Dataset)
|
686
|
-
"#{create_view_prefix_sql(name, options)} AS #{source}"
|
700
|
+
sql = "#{create_view_prefix_sql(name, options)} AS #{source}"
|
701
|
+
if check = options[:check]
|
702
|
+
sql << " WITH#{' LOCAL' if check == :local} CHECK OPTION"
|
703
|
+
end
|
704
|
+
sql
|
687
705
|
end
|
688
706
|
|
689
707
|
# Append the column list to the SQL, if a column list is given.
|
@@ -198,7 +198,12 @@ module Sequel
|
|
198
198
|
def already_in_transaction?(conn, opts)
|
199
199
|
_trans(conn) && (!supports_savepoints? || !opts[:savepoint])
|
200
200
|
end
|
201
|
-
|
201
|
+
|
202
|
+
# Issue query to begin a new savepoint.
|
203
|
+
def begin_savepoint(conn, opts)
|
204
|
+
log_connection_execute(conn, begin_savepoint_sql(savepoint_level(conn)-1))
|
205
|
+
end
|
206
|
+
|
202
207
|
# SQL to start a new savepoint
|
203
208
|
def begin_savepoint_sql(depth)
|
204
209
|
SQL_SAVEPOINT % depth
|
@@ -213,9 +218,8 @@ module Sequel
|
|
213
218
|
# Start a new database transaction or a new savepoint on the given connection.
|
214
219
|
def begin_transaction(conn, opts=OPTS)
|
215
220
|
if supports_savepoints?
|
216
|
-
|
217
|
-
|
218
|
-
log_connection_execute(conn, begin_savepoint_sql(depth-1))
|
221
|
+
if savepoint_level(conn) > 1
|
222
|
+
begin_savepoint(conn, opts)
|
219
223
|
else
|
220
224
|
begin_new_transaction(conn, opts)
|
221
225
|
end
|
@@ -279,7 +279,7 @@ module Sequel
|
|
279
279
|
raise(Error, IMPORT_ERROR_MSG) if columns.empty?
|
280
280
|
ds = opts[:server] ? server(opts[:server]) : self
|
281
281
|
|
282
|
-
if slice_size = opts
|
282
|
+
if slice_size = opts.fetch(:commit_every, opts.fetch(:slice, default_import_slice))
|
283
283
|
offset = 0
|
284
284
|
rows = []
|
285
285
|
while offset < values.length
|
@@ -572,13 +572,13 @@ module Sequel
|
|
572
572
|
# Returns a hash with key_column values as keys and an array of value_column values.
|
573
573
|
# Similar to to_hash_groups, but only selects the columns given.
|
574
574
|
#
|
575
|
-
# DB[:table].
|
575
|
+
# DB[:table].select_hash_groups(:name, :id) # SELECT id, name FROM table
|
576
576
|
# # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
|
577
577
|
#
|
578
578
|
# You can also provide an array of column names for either the key_column,
|
579
579
|
# the value column, or both:
|
580
580
|
#
|
581
|
-
# DB[:table].
|
581
|
+
# DB[:table].select_hash_groups([:first, :middle], [:last, :id]) # SELECT * FROM table
|
582
582
|
# # {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
|
583
583
|
#
|
584
584
|
# When using this method, you must be sure that each expression has an alias
|
@@ -708,19 +708,19 @@ module Sequel
|
|
708
708
|
# array of column values. If the value_column is not given or nil, uses
|
709
709
|
# the entire hash as the value.
|
710
710
|
#
|
711
|
-
# DB[:table].
|
711
|
+
# DB[:table].to_hash_groups(:name, :id) # SELECT * FROM table
|
712
712
|
# # {'Jim'=>[1, 4, 16, ...], 'Bob'=>[2], ...}
|
713
713
|
#
|
714
|
-
# DB[:table].
|
714
|
+
# DB[:table].to_hash_groups(:name) # SELECT * FROM table
|
715
715
|
# # {'Jim'=>[{:id=>1, :name=>'Jim'}, {:id=>4, :name=>'Jim'}, ...], 'Bob'=>[{:id=>2, :name=>'Bob'}], ...}
|
716
716
|
#
|
717
717
|
# You can also provide an array of column names for either the key_column,
|
718
718
|
# the value column, or both:
|
719
719
|
#
|
720
|
-
# DB[:table].
|
720
|
+
# DB[:table].to_hash_groups([:first, :middle], [:last, :id]) # SELECT * FROM table
|
721
721
|
# # {['Jim', 'Bob']=>[['Smith', 1], ['Jackson', 4], ...], ...}
|
722
722
|
#
|
723
|
-
# DB[:table].
|
723
|
+
# DB[:table].to_hash_groups([:first, :middle]) # SELECT * FROM table
|
724
724
|
# # {['Jim', 'Bob']=>[{:id=>1, :first=>'Jim', :middle=>'Bob', :last=>'Smith'}, ...], ...}
|
725
725
|
def to_hash_groups(key_column, value_column = nil)
|
726
726
|
h = {}
|
@@ -889,6 +889,12 @@ module Sequel
|
|
889
889
|
end
|
890
890
|
end
|
891
891
|
|
892
|
+
# The default number of rows that can be inserted in a single INSERT statement via import.
|
893
|
+
# The default is for no limit.
|
894
|
+
def default_import_slice
|
895
|
+
nil
|
896
|
+
end
|
897
|
+
|
892
898
|
# Set the server to use to :default unless it is already set in the passed opts
|
893
899
|
def default_server_opts(opts)
|
894
900
|
{:server=>@opts[:server] || :default}.merge(opts)
|
@@ -54,6 +54,13 @@ module Sequel
|
|
54
54
|
false
|
55
55
|
end
|
56
56
|
|
57
|
+
# Whether the database supports derived column lists (e.g.
|
58
|
+
# "table_expr AS table_alias(column_alias1, column_alias2, ...)"), true by
|
59
|
+
# default.
|
60
|
+
def supports_derived_column_lists?
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
57
64
|
# Whether the dataset supports or can emulate the DISTINCT ON clause, false by default.
|
58
65
|
def supports_distinct_on?
|
59
66
|
false
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -213,10 +213,13 @@ module Sequel
|
|
213
213
|
#
|
214
214
|
# ds.from_self(:alias=>:foo)
|
215
215
|
# # SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo
|
216
|
+
#
|
217
|
+
# ds.from_self(:alias=>:foo, :column_aliases=>[:c1, :c2])
|
218
|
+
# # SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo(c1, c2)
|
216
219
|
def from_self(opts=OPTS)
|
217
220
|
fs = {}
|
218
221
|
@opts.keys.each{|k| fs[k] = nil unless NON_SQL_OPTIONS.include?(k)}
|
219
|
-
clone(fs).from(opts[:alias] ? as(opts[:alias]) : self)
|
222
|
+
clone(fs).from(opts[:alias] ? as(opts[:alias], opts[:column_aliases]) : self)
|
220
223
|
end
|
221
224
|
|
222
225
|
# Match any of the columns to any of the patterns. The terms can be
|
@@ -237,19 +240,23 @@ module Sequel
|
|
237
240
|
# Examples:
|
238
241
|
#
|
239
242
|
# dataset.grep(:a, '%test%')
|
240
|
-
# # SELECT * FROM items WHERE (a LIKE '%test%')
|
243
|
+
# # SELECT * FROM items WHERE (a LIKE '%test%' ESCAPE '\')
|
241
244
|
#
|
242
245
|
# dataset.grep([:a, :b], %w'%test% foo')
|
243
|
-
# # SELECT * FROM items WHERE ((a LIKE '%test%'
|
246
|
+
# # SELECT * FROM items WHERE ((a LIKE '%test%' ESCAPE '\') OR (a LIKE 'foo' ESCAPE '\')
|
247
|
+
# # OR (b LIKE '%test%' ESCAPE '\') OR (b LIKE 'foo' ESCAPE '\'))
|
244
248
|
#
|
245
249
|
# dataset.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true)
|
246
|
-
# # SELECT * FROM a WHERE (((a LIKE '%foo%'
|
250
|
+
# # SELECT * FROM a WHERE (((a LIKE '%foo%' ESCAPE '\') OR (b LIKE '%foo%' ESCAPE '\'))
|
251
|
+
# # AND ((a LIKE '%bar%' ESCAPE '\') OR (b LIKE '%bar%' ESCAPE '\')))
|
247
252
|
#
|
248
253
|
# dataset.grep([:a, :b], %w'%foo% %bar%', :all_columns=>true)
|
249
|
-
# # SELECT * FROM a WHERE (((a LIKE '%foo%'
|
254
|
+
# # SELECT * FROM a WHERE (((a LIKE '%foo%' ESCAPE '\') OR (a LIKE '%bar%' ESCAPE '\'))
|
255
|
+
# # AND ((b LIKE '%foo%' ESCAPE '\') OR (b LIKE '%bar%' ESCAPE '\')))
|
250
256
|
#
|
251
257
|
# dataset.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true, :all_columns=>true)
|
252
|
-
# # SELECT * FROM a WHERE ((a LIKE '%foo%'
|
258
|
+
# # SELECT * FROM a WHERE ((a LIKE '%foo%' ESCAPE '\') AND (b LIKE '%foo%' ESCAPE '\')
|
259
|
+
# # AND (a LIKE '%bar%' ESCAPE '\') AND (b LIKE '%bar%' ESCAPE '\'))
|
253
260
|
def grep(columns, patterns, opts=OPTS)
|
254
261
|
if opts[:all_patterns]
|
255
262
|
conds = Array(patterns).map do |pat|
|
@@ -445,23 +452,33 @@ module Sequel
|
|
445
452
|
last_alias = options[:implicit_qualifier]
|
446
453
|
qualify_type = options[:qualify]
|
447
454
|
|
448
|
-
if table.is_a?(
|
455
|
+
if table.is_a?(SQL::AliasedExpression)
|
456
|
+
table_expr = if table_alias
|
457
|
+
SQL::AliasedExpression.new(table.expression, table_alias, table.columns)
|
458
|
+
else
|
459
|
+
table
|
460
|
+
end
|
461
|
+
table = table_expr.expression
|
462
|
+
table_name = table_alias = table_expr.alias
|
463
|
+
elsif table.is_a?(Dataset)
|
449
464
|
if table_alias.nil?
|
450
465
|
table_alias_num = (@opts[:num_dataset_sources] || 0) + 1
|
451
466
|
table_alias = dataset_alias(table_alias_num)
|
452
467
|
end
|
453
468
|
table_name = table_alias
|
469
|
+
table_expr = SQL::AliasedExpression.new(table, table_alias)
|
454
470
|
else
|
455
471
|
table, implicit_table_alias = split_alias(table)
|
456
472
|
table_alias ||= implicit_table_alias
|
457
473
|
table_name = table_alias || table
|
474
|
+
table_expr = table_alias ? SQL::AliasedExpression.new(table, table_alias) : table
|
458
475
|
end
|
459
476
|
|
460
477
|
join = if expr.nil? and !block
|
461
|
-
SQL::JoinClause.new(type,
|
478
|
+
SQL::JoinClause.new(type, table_expr)
|
462
479
|
elsif using_join
|
463
480
|
raise(Sequel::Error, "can't use a block if providing an array of symbols as expr") if block
|
464
|
-
SQL::JoinUsingClause.new(expr, type,
|
481
|
+
SQL::JoinUsingClause.new(expr, type, table_expr)
|
465
482
|
else
|
466
483
|
last_alias ||= @opts[:last_joined_table] || first_source_alias
|
467
484
|
if Sequel.condition_specifier?(expr)
|
@@ -485,7 +502,7 @@ module Sequel
|
|
485
502
|
expr2 = yield(table_name, last_alias, @opts[:join] || [])
|
486
503
|
expr = expr ? SQL::BooleanExpression.new(:AND, expr, expr2) : expr2
|
487
504
|
end
|
488
|
-
SQL::JoinOnClause.new(expr, type,
|
505
|
+
SQL::JoinOnClause.new(expr, type, table_expr)
|
489
506
|
end
|
490
507
|
|
491
508
|
opts = {:join => (@opts[:join] || []) + [join], :last_joined_table => table_name}
|
@@ -890,7 +907,7 @@ module Sequel
|
|
890
907
|
# :recursive :: Specify that this is a recursive CTE
|
891
908
|
#
|
892
909
|
# DB[:items].with(:items, DB[:syx].where(:name.like('A%')))
|
893
|
-
# # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%')) SELECT * FROM items
|
910
|
+
# # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
|
894
911
|
def with(name, dataset, opts=OPTS)
|
895
912
|
raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
|
896
913
|
if hoist_cte?(dataset)
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -5,7 +5,7 @@ module Sequel
|
|
5
5
|
# These are methods you can call to see what SQL will be generated by the dataset.
|
6
6
|
# ---------------------
|
7
7
|
|
8
|
-
# Returns an EXISTS clause for the dataset as
|
8
|
+
# Returns an EXISTS clause for the dataset as an SQL::PlaceholderLiteralString.
|
9
9
|
#
|
10
10
|
# DB.select(1).where(DB[:items].exists)
|
11
11
|
# # SELECT 1 WHERE (EXISTS (SELECT * FROM items))
|
@@ -274,6 +274,7 @@ module Sequel
|
|
274
274
|
ESCAPE = " ESCAPE ".freeze
|
275
275
|
EXTRACT = 'extract('.freeze
|
276
276
|
EXISTS = ['EXISTS '.freeze].freeze
|
277
|
+
FILTER = " FILTER (WHERE ".freeze
|
277
278
|
FOR_UPDATE = ' FOR UPDATE'.freeze
|
278
279
|
FORMAT_DATE = "'%Y-%m-%d'".freeze
|
279
280
|
FORMAT_DATE_STANDARD = "DATE '%Y-%m-%d'".freeze
|
@@ -284,7 +285,7 @@ module Sequel
|
|
284
285
|
FRAME_ALL = "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING".freeze
|
285
286
|
FRAME_ROWS = "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW".freeze
|
286
287
|
FROM = ' FROM '.freeze
|
287
|
-
|
288
|
+
FUNCTION_DISTINCT = "DISTINCT ".freeze
|
288
289
|
GROUP_BY = " GROUP BY ".freeze
|
289
290
|
HAVING = " HAVING ".freeze
|
290
291
|
INSERT = "INSERT".freeze
|
@@ -332,6 +333,8 @@ module Sequel
|
|
332
333
|
VALUES = " VALUES ".freeze
|
333
334
|
V190 = '1.9.0'.freeze
|
334
335
|
WHERE = " WHERE ".freeze
|
336
|
+
WITH_ORDINALITY = " WITH ORDINALITY".freeze
|
337
|
+
WITHIN_GROUP = " WITHIN GROUP (ORDER BY ".freeze
|
335
338
|
|
336
339
|
[:literal, :quote_identifier, :quote_schema_table].each do |meth|
|
337
340
|
class_eval(<<-END, __FILE__, __LINE__ + 1)
|
@@ -346,7 +349,7 @@ module Sequel
|
|
346
349
|
# Append literalization of aliased expression to SQL string.
|
347
350
|
def aliased_expression_sql_append(sql, ae)
|
348
351
|
literal_append(sql, ae.expression)
|
349
|
-
as_sql_append(sql, ae.alias)
|
352
|
+
as_sql_append(sql, ae.alias, ae.columns)
|
350
353
|
end
|
351
354
|
|
352
355
|
# Append literalization of array to SQL string.
|
@@ -522,28 +525,87 @@ module Sequel
|
|
522
525
|
end
|
523
526
|
end
|
524
527
|
|
525
|
-
#
|
526
|
-
# By default, assumes just the function name may need to
|
527
|
-
# be emulated, adapters should set an EMULATED_FUNCTION_MAP
|
528
|
-
# hash mapping emulated functions to native functions in
|
529
|
-
# their dataset class to setup the emulation.
|
528
|
+
# REMOVE411
|
530
529
|
def emulated_function_sql_append(sql, f)
|
531
530
|
_function_sql_append(sql, native_function_name(f.f), f.args)
|
532
531
|
end
|
533
532
|
|
534
533
|
# Append literalization of function call to SQL string.
|
535
534
|
def function_sql_append(sql, f)
|
536
|
-
|
535
|
+
name = f.name
|
536
|
+
opts = f.opts
|
537
|
+
|
538
|
+
if opts[:emulate]
|
539
|
+
if emulate_function?(name)
|
540
|
+
emulate_function_sql_append(sql, f)
|
541
|
+
return
|
542
|
+
end
|
543
|
+
|
544
|
+
name = native_function_name(name)
|
545
|
+
end
|
546
|
+
|
547
|
+
sql << LATERAL if opts[:lateral]
|
548
|
+
|
549
|
+
case name
|
550
|
+
when SQL::Identifier
|
551
|
+
if supports_quoted_function_names? && opts[:quoted] != false
|
552
|
+
literal_append(sql, name)
|
553
|
+
else
|
554
|
+
sql << name.value.to_s
|
555
|
+
end
|
556
|
+
when SQL::QualifiedIdentifier
|
557
|
+
if supports_quoted_function_names? && opts[:quoted] != false
|
558
|
+
literal_append(sql, name)
|
559
|
+
else
|
560
|
+
sql << split_qualifiers(name).join(DOT)
|
561
|
+
end
|
562
|
+
else
|
563
|
+
if supports_quoted_function_names? && opts[:quoted]
|
564
|
+
quote_identifier_append(sql, name)
|
565
|
+
else
|
566
|
+
sql << name.to_s
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
sql << PAREN_OPEN
|
571
|
+
if opts[:*]
|
572
|
+
sql << WILDCARD
|
573
|
+
else
|
574
|
+
sql << FUNCTION_DISTINCT if opts[:distinct]
|
575
|
+
expression_list_append(sql, f.args)
|
576
|
+
end
|
577
|
+
sql << PAREN_CLOSE
|
578
|
+
|
579
|
+
if group = opts[:within_group]
|
580
|
+
sql << WITHIN_GROUP
|
581
|
+
expression_list_append(sql, group)
|
582
|
+
sql << PAREN_CLOSE
|
583
|
+
end
|
584
|
+
|
585
|
+
if filter = opts[:filter]
|
586
|
+
sql << FILTER
|
587
|
+
literal_append(sql, filter_expr(filter, &opts[:filter_block]))
|
588
|
+
sql << PAREN_CLOSE
|
589
|
+
end
|
590
|
+
|
591
|
+
if window = opts[:over]
|
592
|
+
sql << OVER
|
593
|
+
window_sql_append(sql, window.opts)
|
594
|
+
end
|
595
|
+
|
596
|
+
if opts[:with_ordinality]
|
597
|
+
sql << WITH_ORDINALITY
|
598
|
+
end
|
537
599
|
end
|
538
600
|
|
539
601
|
# Append literalization of JOIN clause without ON or USING to SQL string.
|
540
602
|
def join_clause_sql_append(sql, jc)
|
541
603
|
table = jc.table
|
542
604
|
table_alias = jc.table_alias
|
543
|
-
table_alias = nil if table == table_alias
|
605
|
+
table_alias = nil if table == table_alias && !jc.column_aliases
|
544
606
|
sql << SPACE << join_type_sql(jc.join_type) << SPACE
|
545
607
|
identifier_append(sql, table)
|
546
|
-
as_sql_append(sql, table_alias) if table_alias
|
608
|
+
as_sql_append(sql, table_alias, jc.column_aliases) if table_alias
|
547
609
|
end
|
548
610
|
|
549
611
|
# Append literalization of JOIN ON clause to SQL string.
|
@@ -765,8 +827,9 @@ module Sequel
|
|
765
827
|
sql << PAREN_CLOSE
|
766
828
|
end
|
767
829
|
|
768
|
-
#
|
830
|
+
# REMOVE411
|
769
831
|
def window_function_sql_append(sql, function, window)
|
832
|
+
Deprecation.deprecate("Dataset#window_function_sql_append", "Please use Sequel::SQL::Function.new(name, *args).over(...) to create an SQL window function")
|
770
833
|
literal_append(sql, function)
|
771
834
|
sql << OVER
|
772
835
|
literal_append(sql, window)
|
@@ -782,8 +845,9 @@ module Sequel
|
|
782
845
|
|
783
846
|
private
|
784
847
|
|
785
|
-
#
|
848
|
+
# REMOVE411
|
786
849
|
def _function_sql_append(sql, name, args)
|
850
|
+
Deprecation.deprecate("Dataset#emulated_function_sql_append and #_function_sql_append", "Please use Sequel::SQL::Function.new!(name, args, :emulate=>true) to create an emulated SQL function")
|
787
851
|
case name
|
788
852
|
when SQL::Identifier
|
789
853
|
if supports_quoted_function_names?
|
@@ -857,9 +921,15 @@ module Sequel
|
|
857
921
|
end
|
858
922
|
|
859
923
|
# Append aliasing expression to SQL string.
|
860
|
-
def as_sql_append(sql, aliaz)
|
924
|
+
def as_sql_append(sql, aliaz, column_aliases=nil)
|
861
925
|
sql << AS
|
862
926
|
quote_identifier_append(sql, aliaz)
|
927
|
+
if column_aliases
|
928
|
+
raise Error, "#{db.database_type} does not support derived column lists" unless supports_derived_column_lists?
|
929
|
+
sql << PAREN_OPEN
|
930
|
+
identifier_list_append(sql, column_aliases)
|
931
|
+
sql << PAREN_CLOSE
|
932
|
+
end
|
863
933
|
end
|
864
934
|
|
865
935
|
# Raise an InvalidOperation exception if deletion is not allowed
|
@@ -960,6 +1030,12 @@ module Sequel
|
|
960
1030
|
nil
|
961
1031
|
end
|
962
1032
|
|
1033
|
+
# Whether to emulate the function with the given name. This should only be true
|
1034
|
+
# if the emulation goes beyond choosing a function with a different name.
|
1035
|
+
def emulate_function?(name)
|
1036
|
+
false
|
1037
|
+
end
|
1038
|
+
|
963
1039
|
# Append literalization of array of expressions to SQL string.
|
964
1040
|
def expression_list_append(sql, columns)
|
965
1041
|
c = false
|