sequel 4.39.0 → 4.40.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +34 -0
- data/README.rdoc +8 -4
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +7 -7
- data/doc/association_basics.rdoc +7 -7
- data/doc/cheat_sheet.rdoc +5 -3
- data/doc/core_extensions.rdoc +3 -3
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/object_model.rdoc +16 -7
- data/doc/postgresql.rdoc +3 -3
- data/doc/querying.rdoc +3 -3
- data/doc/release_notes/4.40.0.txt +179 -0
- data/doc/security.rdoc +2 -1
- data/doc/sql.rdoc +34 -18
- data/doc/testing.rdoc +1 -0
- data/doc/virtual_rows.rdoc +11 -2
- data/lib/sequel/adapters/jdbc/derby.rb +7 -1
- data/lib/sequel/adapters/jdbc/h2.rb +15 -1
- data/lib/sequel/adapters/oracle.rb +9 -5
- data/lib/sequel/adapters/postgres.rb +0 -1
- data/lib/sequel/adapters/shared/cubrid.rb +11 -11
- data/lib/sequel/adapters/shared/db2.rb +4 -8
- data/lib/sequel/adapters/shared/mssql.rb +41 -28
- data/lib/sequel/adapters/shared/mysql.rb +9 -6
- data/lib/sequel/adapters/shared/oracle.rb +16 -5
- data/lib/sequel/adapters/shared/postgres.rb +84 -45
- data/lib/sequel/adapters/shared/sqlanywhere.rb +29 -15
- data/lib/sequel/adapters/shared/sqlite.rb +6 -6
- data/lib/sequel/core.rb +61 -10
- data/lib/sequel/database/connecting.rb +2 -1
- data/lib/sequel/database/features.rb +7 -0
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +30 -3
- data/lib/sequel/database/transactions.rb +4 -2
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/graph.rb +6 -1
- data/lib/sequel/dataset/query.rb +14 -7
- data/lib/sequel/dataset/sql.rb +2 -2
- data/lib/sequel/extensions/core_extensions.rb +2 -1
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/s.rb +57 -0
- data/lib/sequel/extensions/set_overrides.rb +5 -1
- data/lib/sequel/extensions/sql_expr.rb +1 -0
- data/lib/sequel/extensions/symbol_aref.rb +71 -0
- data/lib/sequel/extensions/symbol_aref_refinement.rb +41 -0
- data/lib/sequel/extensions/symbol_as.rb +23 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +35 -0
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/plugins/class_table_inheritance.rb +14 -3
- data/lib/sequel/plugins/column_select.rb +4 -2
- data/lib/sequel/plugins/dataset_associations.rb +12 -4
- data/lib/sequel/plugins/insert_returning_select.rb +1 -1
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/prepared_statements.rb +1 -0
- data/lib/sequel/sql.rb +40 -8
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/firebird_spec.rb +3 -3
- data/spec/adapters/mssql_spec.rb +40 -40
- data/spec/adapters/mysql_spec.rb +5 -5
- data/spec/adapters/oracle_spec.rb +4 -4
- data/spec/adapters/postgres_spec.rb +135 -124
- data/spec/adapters/spec_helper.rb +1 -0
- data/spec/adapters/sqlite_spec.rb +6 -6
- data/spec/core/dataset_spec.rb +2 -2
- data/spec/core/expression_filters_spec.rb +43 -2
- data/spec/core/schema_spec.rb +35 -1
- data/spec/core_extensions_spec.rb +27 -0
- data/spec/extensions/class_table_inheritance_spec.rb +8 -0
- data/spec/extensions/column_select_spec.rb +8 -0
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +9 -0
- data/spec/extensions/insert_returning_select_spec.rb +20 -0
- data/spec/extensions/prepared_statements_spec.rb +7 -0
- data/spec/extensions/s_spec.rb +60 -0
- data/spec/extensions/symbol_aref_refinement_spec.rb +28 -0
- data/spec/extensions/symbol_as_refinement_spec.rb +21 -0
- data/spec/integration/associations_test.rb +62 -57
- data/spec/integration/dataset_test.rb +54 -54
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/plugin_test.rb +20 -20
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +21 -0
- data/spec/integration/spec_helper.rb +1 -0
- metadata +12 -2
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The symbol_as extension adds Symbol#as, for creating
|
4
|
+
# Sequel::SQL::AliasedExpression objects. It's
|
5
|
+
# designed as a shortcut so that instead of:
|
6
|
+
#
|
7
|
+
# Sequel[:column].as(:alias)
|
8
|
+
#
|
9
|
+
# you can just write:
|
10
|
+
#
|
11
|
+
# :column.as(:alias)
|
12
|
+
#
|
13
|
+
# To load the extension:
|
14
|
+
#
|
15
|
+
# Sequel.extension :symbol_as
|
16
|
+
#
|
17
|
+
# If you are using Ruby 2+, and you would like to use refinements, there
|
18
|
+
# is a refinement version of this in the symbol_as_refinement extension.
|
19
|
+
|
20
|
+
#
|
21
|
+
class Symbol
|
22
|
+
include Sequel::SQL::AliasMethods
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The symbol_as_refinement extension adds a refinement that makes
|
4
|
+
# Symbol#as return Sequel::SQL::AliasedExpression instances. It's
|
5
|
+
# designed as a shortcut so that instead of:
|
6
|
+
#
|
7
|
+
# Sequel[:column].as(:alias) # column AS alias
|
8
|
+
#
|
9
|
+
# you can just write:
|
10
|
+
#
|
11
|
+
# :column.as(:alias) # column AS alias
|
12
|
+
#
|
13
|
+
# To load the extension:
|
14
|
+
#
|
15
|
+
# Sequel.extension :symbol_as_refinement
|
16
|
+
#
|
17
|
+
# To enable the refinement for the current file:
|
18
|
+
#
|
19
|
+
# using Sequel::SymbolAs
|
20
|
+
#
|
21
|
+
# If you would like this extension to be enabled globally instead
|
22
|
+
# of as a refinement, use the symbol_as extension.
|
23
|
+
#
|
24
|
+
# Related module: Sequel::SymbolAs
|
25
|
+
|
26
|
+
raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
|
27
|
+
|
28
|
+
module Sequel::SymbolAs
|
29
|
+
refine Symbol do
|
30
|
+
def as(aliaz, columns=nil)
|
31
|
+
Sequel::SQL::AliasedExpression.new(self, aliaz, columns)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1614,12 +1614,12 @@ module Sequel
|
|
1614
1614
|
model.primary_key_hash(pk)
|
1615
1615
|
end
|
1616
1616
|
|
1617
|
-
# Returns a hash mapping the receivers primary key column(s) to their values.
|
1617
|
+
# Returns a hash mapping the receivers qualified primary key column(s) to their values.
|
1618
1618
|
#
|
1619
1619
|
# Artist[1].qualified_pk_hash
|
1620
|
-
# # => {Sequel
|
1620
|
+
# # => {Sequel[:artists][:id]=>1}
|
1621
1621
|
# Artist[[1, 2]].qualified_pk_hash
|
1622
|
-
# # => {Sequel
|
1622
|
+
# # => {Sequel[:artists][:id1]=>1, Sequel[:artists][:id2]=>2}
|
1623
1623
|
def qualified_pk_hash(qualifier=model.table_name)
|
1624
1624
|
model.qualified_primary_key_hash(pk, qualifier)
|
1625
1625
|
end
|
@@ -31,9 +31,15 @@ module Sequel
|
|
31
31
|
# The class_table_inheritance plugin assumes that the root table
|
32
32
|
# (e.g. employees) has a primary key column (usually autoincrementing),
|
33
33
|
# and all other tables have a foreign key of the same name that points
|
34
|
-
# to the same column in their superclass's table
|
35
|
-
#
|
36
|
-
# other table is a foreign key
|
34
|
+
# to the same column in their superclass's table, which is also the primary
|
35
|
+
# key for that table. In this example, the employees table has an id column
|
36
|
+
# is a primary key and the id column in every other table is a foreign key
|
37
|
+
# referencing employees.id, which is also the primary key of that table.
|
38
|
+
#
|
39
|
+
# Additionally, note that other than the primary key column, no subclass
|
40
|
+
# table has a column with the same name as any superclass table. This plugin
|
41
|
+
# does not support cases where the column names in a subclass table overlap
|
42
|
+
# with any column names in a superclass table.
|
37
43
|
#
|
38
44
|
# In this example the staff table also stores Cook model objects and the
|
39
45
|
# executives table also stores CEO model objects.
|
@@ -354,6 +360,11 @@ module Sequel
|
|
354
360
|
cti_this(m).update(h) unless h.empty?
|
355
361
|
end
|
356
362
|
end
|
363
|
+
|
364
|
+
# Don't allow use of prepared statements.
|
365
|
+
def use_prepared_statements_for?(type)
|
366
|
+
false
|
367
|
+
end
|
357
368
|
end
|
358
369
|
end
|
359
370
|
end
|
@@ -48,8 +48,10 @@ module Sequel
|
|
48
48
|
cols = cols.map{|c, _| c}
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
52
|
-
|
51
|
+
|
52
|
+
if cols ||= check_non_connection_error{ds.columns}
|
53
|
+
ds = ds.select(*cols.map{|c| Sequel.qualify(ds.first_source, Sequel.identifier(c))})
|
54
|
+
end
|
53
55
|
end
|
54
56
|
ds
|
55
57
|
end
|
@@ -93,12 +93,20 @@ module Sequel
|
|
93
93
|
where(r.qualify(r.join_table_alias, r[:left_keys])=>sds.select(*r.qualify(model.table_name, r[:left_primary_key_columns])))
|
94
94
|
ds.filter(r.qualified_right_primary_key=>r.send(:apply_filter_by_associations_limit_strategy, mds))
|
95
95
|
when :many_through_many, :one_through_many
|
96
|
-
fre = r.reverse_edges.first
|
97
96
|
fe, *edges = r.edges
|
98
97
|
edges << r.final_edge
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
if fre = r.reverse_edges.first
|
99
|
+
table = fre[:table]
|
100
|
+
left = fre[:left]
|
101
|
+
mds = model.join(fe[:table], Array(fe[:right]).zip(Array(fe[:left])), :implicit_qualifier=>model.table_name)
|
102
|
+
else
|
103
|
+
table = fe[:table]
|
104
|
+
left = edges.first[:left]
|
105
|
+
edges = []
|
106
|
+
mds = r.associated_dataset
|
107
|
+
end
|
108
|
+
mds = mds.
|
109
|
+
select(*Array(r.qualify(table, left))).
|
102
110
|
where(r.qualify(fe[:table], fe[:right])=>sds.select(*r.qualify(model.table_name, r[:left_primary_key_columns])))
|
103
111
|
edges.each{|e| mds = mds.join(e[:table], Array(e[:right]).zip(Array(e[:left])))}
|
104
112
|
ds.filter(r.qualified_right_primary_key=>r.send(:apply_filter_by_associations_limit_strategy, mds))
|
@@ -35,7 +35,7 @@ module Sequel
|
|
35
35
|
# When reseting the instance dataset, also reset the instance_insert_dataset.
|
36
36
|
def reset_instance_dataset
|
37
37
|
ret = super
|
38
|
-
ds = @instance_dataset
|
38
|
+
return unless ds = @instance_dataset
|
39
39
|
|
40
40
|
if columns = insert_returning_columns(ds)
|
41
41
|
ds = ds.returning(*columns)
|
@@ -84,7 +84,7 @@ module Sequel
|
|
84
84
|
def _update_without_checking(columns)
|
85
85
|
ds = _update_dataset
|
86
86
|
lc = model.lock_column
|
87
|
-
rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel
|
87
|
+
rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns))).all
|
88
88
|
values[lc] = rows.first[lc] unless rows.empty?
|
89
89
|
rows.length
|
90
90
|
end
|
data/lib/sequel/sql.rb
CHANGED
@@ -206,6 +206,9 @@ module Sequel
|
|
206
206
|
# Operator symbols that take one or more arguments
|
207
207
|
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS - [:**]
|
208
208
|
|
209
|
+
# Operator symbols that are associative
|
210
|
+
ASSOCIATIVE_OPERATORS = [:AND, :OR, :'||', :+, :*, :&, :|]
|
211
|
+
|
209
212
|
# Operator symbols that take only a single argument
|
210
213
|
ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
|
211
214
|
|
@@ -229,9 +232,12 @@ module Sequel
|
|
229
232
|
case op
|
230
233
|
when *N_ARITY_OPERATORS
|
231
234
|
raise(Error, "The #{op} operator requires at least 1 argument") unless args.length >= 1
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
+
args.map!{|a| a.is_a?(self.class) && a.op == :NOOP ? a.args.first : a}
|
236
|
+
if ASSOCIATIVE_OPERATORS.include?(op)
|
237
|
+
old_args = args
|
238
|
+
args = []
|
239
|
+
old_args.each{|a| a.is_a?(self.class) && a.op == op ? args.concat(a.args) : args.push(a)}
|
240
|
+
end
|
235
241
|
when *TWO_ARITY_OPERATORS
|
236
242
|
raise(Error, "The #{op} operator requires precisely 2 arguments") unless args.length == 2
|
237
243
|
# With IN/NOT IN, even if the second argument is an array of two element arrays,
|
@@ -944,12 +950,21 @@ module Sequel
|
|
944
950
|
|
945
951
|
# Qualify the receiver with the given +qualifier+ (table for column/schema for table).
|
946
952
|
#
|
947
|
-
# Sequel[:column].qualify(:table)
|
948
|
-
# Sequel[:table].qualify(:schema)
|
953
|
+
# Sequel[:column].qualify(:table) # "table"."column"
|
954
|
+
# Sequel[:table].qualify(:schema) # "schema"."table"
|
949
955
|
# Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
|
950
956
|
def qualify(qualifier)
|
951
957
|
QualifiedIdentifier.new(qualifier, self)
|
952
958
|
end
|
959
|
+
|
960
|
+
# Qualify the receiver with the given +qualifier+ (table for column/schema for table).
|
961
|
+
#
|
962
|
+
# Sequel[:table][:column] # "table"."column"
|
963
|
+
# Sequel[:schema][:table] # "schema"."table"
|
964
|
+
# Sequel[:schema][:table][:column] # "schema"."table"."column"
|
965
|
+
def [](identifier)
|
966
|
+
QualifiedIdentifier.new(self, identifier)
|
967
|
+
end
|
953
968
|
end
|
954
969
|
|
955
970
|
# This module includes the +like+ and +ilike+ methods used for pattern matching that are defined on objects that can be
|
@@ -1635,7 +1650,8 @@ module Sequel
|
|
1635
1650
|
|
1636
1651
|
# Set the table and column to the given arguments
|
1637
1652
|
def initialize(table, column)
|
1638
|
-
@table
|
1653
|
+
@table = convert_identifier(table)
|
1654
|
+
@column = convert_identifier(column)
|
1639
1655
|
end
|
1640
1656
|
|
1641
1657
|
# Create a Function using this identifier as the functions name, with
|
@@ -1645,6 +1661,18 @@ module Sequel
|
|
1645
1661
|
end
|
1646
1662
|
|
1647
1663
|
to_s_method :qualified_identifier_sql, "@table, @column"
|
1664
|
+
|
1665
|
+
private
|
1666
|
+
|
1667
|
+
# Automatically convert SQL::Identifiers to strings
|
1668
|
+
def convert_identifier(identifier)
|
1669
|
+
case identifier
|
1670
|
+
when SQL::Identifier
|
1671
|
+
identifier.value.to_s
|
1672
|
+
else
|
1673
|
+
identifier
|
1674
|
+
end
|
1675
|
+
end
|
1648
1676
|
end
|
1649
1677
|
|
1650
1678
|
# Subclass of +ComplexExpression+ where the expression results
|
@@ -1869,8 +1897,12 @@ module Sequel
|
|
1869
1897
|
end
|
1870
1898
|
end
|
1871
1899
|
elsif args.empty?
|
1872
|
-
|
1873
|
-
|
1900
|
+
if Sequel.split_symbols?
|
1901
|
+
table, column = m.to_s.split(DOUBLE_UNDERSCORE, 2)
|
1902
|
+
column ? QualifiedIdentifier.new(table, column) : Identifier.new(m)
|
1903
|
+
else
|
1904
|
+
Identifier.new(m)
|
1905
|
+
end
|
1874
1906
|
else
|
1875
1907
|
Function.new(m, *args)
|
1876
1908
|
end
|
data/lib/sequel/version.rb
CHANGED
@@ -5,7 +5,7 @@ module Sequel
|
|
5
5
|
MAJOR = 4
|
6
6
|
# The minor version of Sequel. Bumped for every non-patch level
|
7
7
|
# release, generally around once a month.
|
8
|
-
MINOR =
|
8
|
+
MINOR = 40
|
9
9
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
10
10
|
# releases that fix regressions from previous versions.
|
11
11
|
TINY = 0
|
@@ -125,7 +125,7 @@ describe "A Firebird dataset" do
|
|
125
125
|
@d.select(:now[]).sql.must_equal \
|
126
126
|
'SELECT now() FROM "TEST"'
|
127
127
|
|
128
|
-
@d.select(:max[:
|
128
|
+
@d.select(:max[Sequel[:items][:val]]).sql.must_equal \
|
129
129
|
'SELECT max("ITEMS"."VAL") FROM "TEST"'
|
130
130
|
|
131
131
|
@d.order(:name.desc).sql.must_equal \
|
@@ -143,10 +143,10 @@ describe "A Firebird dataset" do
|
|
143
143
|
@d.select(:test[:ABC, 'hello']).sql.must_equal \
|
144
144
|
"SELECT test(\"ABC\", 'hello') FROM \"TEST\""
|
145
145
|
|
146
|
-
@d.select(:test[:
|
146
|
+
@d.select(:test[Sequel[:ABC][:DEF], 'hello']).sql.must_equal \
|
147
147
|
"SELECT test(\"ABC\".\"DEF\", 'hello') FROM \"TEST\""
|
148
148
|
|
149
|
-
@d.select(:test[:
|
149
|
+
@d.select(:test[Sequel[:ABC][:DEF], 'hello'].as(:X2)).sql.must_equal \
|
150
150
|
"SELECT test(\"ABC\".\"DEF\", 'hello') AS \"X2\" FROM \"TEST\""
|
151
151
|
|
152
152
|
@d.insert_sql(:val => 333).must_match \
|
data/spec/adapters/mssql_spec.rb
CHANGED
@@ -62,11 +62,11 @@ describe "MSSQL" do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
it "should should support CROSS APPLY" do
|
65
|
-
@db[:test3].cross_apply(@db[:test4].where(:
|
65
|
+
@db[:test3].cross_apply(@db[:test4].where(Sequel[:test3][:v3]=>Sequel[:test4][:v4])).select_order_map([:v3, :v4]).must_equal [[1,1]]
|
66
66
|
end
|
67
67
|
|
68
68
|
it "should should support OUTER APPLY" do
|
69
|
-
@db[:test3].outer_apply(@db[:test4].where(:
|
69
|
+
@db[:test3].outer_apply(@db[:test4].where(Sequel[:test3][:v3]=>Sequel[:test4][:v4])).select_order_map([:v3, :v4]).must_equal [[1,1], [2, nil]]
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -105,7 +105,7 @@ describe "MSSQL Dataset#join_table" do
|
|
105
105
|
['SELECT * FROM [ITEMS] INNER JOIN [CATEGORIES] ON (([CATEGORIES].[ID1] = [ITEMS].[ID1]) AND ([CATEGORIES].[ID2] = [ITEMS].[ID2]))',
|
106
106
|
'SELECT * FROM [ITEMS] INNER JOIN [CATEGORIES] ON (([CATEGORIES].[ID2] = [ITEMS].[ID2]) AND ([CATEGORIES].[ID1] = [ITEMS].[ID1]))'].
|
107
107
|
must_include(DB[:items].join(:categories, [:id1, :id2]).sql)
|
108
|
-
DB[:
|
108
|
+
DB[Sequel[:items].as(:i)].join(Sequel[:categories].as(:c), [:id]).sql.must_equal 'SELECT * FROM [ITEMS] AS [I] INNER JOIN [CATEGORIES] AS [C] ON ([C].[ID] = [I].[ID])'
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
@@ -121,56 +121,56 @@ describe "MSSQL Dataset#output" do
|
|
121
121
|
end
|
122
122
|
|
123
123
|
it "should format OUTPUT clauses without INTO for DELETE statements" do
|
124
|
-
@ds.output(nil, [:
|
124
|
+
@ds.output(nil, [Sequel[:deleted][:name], Sequel[:deleted][:value]]).delete_sql.must_match(/DELETE FROM \[ITEMS\] OUTPUT \[DELETED\].\[(NAME|VALUE)\], \[DELETED\].\[(NAME|VALUE)\]/)
|
125
125
|
@ds.output(nil, [Sequel::SQL::ColumnAll.new(:deleted)]).delete_sql.must_match(/DELETE FROM \[ITEMS\] OUTPUT \[DELETED\].*/)
|
126
126
|
end
|
127
127
|
|
128
128
|
it "should format OUTPUT clauses with INTO for DELETE statements" do
|
129
|
-
@ds.output(:out, [:
|
130
|
-
@ds.output(:out, {:name => :
|
129
|
+
@ds.output(:out, [Sequel[:deleted][:name], Sequel[:deleted][:value]]).delete_sql.must_match(/DELETE FROM \[ITEMS\] OUTPUT \[DELETED\].\[(NAME|VALUE)\], \[DELETED\].\[(NAME|VALUE)\] INTO \[OUT\]/)
|
130
|
+
@ds.output(:out, {:name => Sequel[:deleted][:name], :value => Sequel[:deleted][:value]}).delete_sql.must_match(/DELETE FROM \[ITEMS\] OUTPUT \[DELETED\].\[(NAME|VALUE)\], \[DELETED\].\[(NAME|VALUE)\] INTO \[OUT\] \(\[(NAME|VALUE)\], \[(NAME|VALUE)\]\)/)
|
131
131
|
end
|
132
132
|
|
133
133
|
it "should format OUTPUT clauses without INTO for INSERT statements" do
|
134
|
-
@ds.output(nil, [:
|
134
|
+
@ds.output(nil, [Sequel[:inserted][:name], Sequel[:inserted][:value]]).insert_sql(:name => "name", :value => 1).must_match(/INSERT INTO \[ITEMS\] \(\[(NAME|VALUE)\], \[(NAME|VALUE)\]\) OUTPUT \[INSERTED\].\[(NAME|VALUE)\], \[INSERTED\].\[(NAME|VALUE)\] VALUES \((N'name'|1), (N'name'|1)\)/)
|
135
135
|
@ds.output(nil, [Sequel::SQL::ColumnAll.new(:inserted)]).insert_sql(:name => "name", :value => 1).must_match(/INSERT INTO \[ITEMS\] \(\[(NAME|VALUE)\], \[(NAME|VALUE)\]\) OUTPUT \[INSERTED\].* VALUES \((N'name'|1), (N'name'|1)\)/)
|
136
136
|
end
|
137
137
|
|
138
138
|
it "should format OUTPUT clauses with INTO for INSERT statements" do
|
139
|
-
@ds.output(:out, [:
|
140
|
-
@ds.output(:out, {:name => :
|
139
|
+
@ds.output(:out, [Sequel[:inserted][:name], Sequel[:inserted][:value]]).insert_sql(:name => "name", :value => 1).must_match(/INSERT INTO \[ITEMS\] \((\[NAME\]|\[VALUE\]), (\[NAME\]|\[VALUE\])\) OUTPUT \[INSERTED\].\[(NAME|VALUE)\], \[INSERTED\].\[(NAME|VALUE)\] INTO \[OUT\] VALUES \((N'name'|1), (N'name'|1)\)/)
|
140
|
+
@ds.output(:out, {:name => Sequel[:inserted][:name], :value => Sequel[:inserted][:value]}).insert_sql(:name => "name", :value => 1).must_match(/INSERT INTO \[ITEMS\] \(\[(NAME|VALUE)\], \[(NAME|VALUE)\]\) OUTPUT \[INSERTED\].\[(NAME|VALUE)\], \[INSERTED\].\[(NAME|VALUE)\] INTO \[OUT\] \(\[(NAME|VALUE)\], \[(NAME|VALUE)\]\) VALUES \((N'name'|1), (N'name'|1)\)/)
|
141
141
|
end
|
142
142
|
|
143
143
|
it "should format OUTPUT clauses without INTO for UPDATE statements" do
|
144
|
-
@ds.output(nil, [:
|
144
|
+
@ds.output(nil, [Sequel[:inserted][:name], Sequel[:deleted][:value]]).update_sql(:value => 2).must_match(/UPDATE \[ITEMS\] SET \[VALUE\] = 2 OUTPUT \[(INSERTED\].\[NAME|DELETED\].\[VALUE)\], \[(INSERTED\].\[NAME|DELETED\].\[VALUE)\]/)
|
145
145
|
@ds.output(nil, [Sequel::SQL::ColumnAll.new(:inserted)]).update_sql(:value => 2).must_match(/UPDATE \[ITEMS\] SET \[VALUE\] = 2 OUTPUT \[INSERTED\].*/)
|
146
146
|
end
|
147
147
|
|
148
148
|
it "should format OUTPUT clauses with INTO for UPDATE statements" do
|
149
|
-
@ds.output(:out, [:
|
150
|
-
@ds.output(:out, {:name => :
|
149
|
+
@ds.output(:out, [Sequel[:inserted][:name], Sequel[:deleted][:value]]).update_sql(:value => 2).must_match(/UPDATE \[ITEMS\] SET \[VALUE\] = 2 OUTPUT \[(INSERTED\].\[NAME|DELETED\].\[VALUE)\], \[(INSERTED\].\[NAME|DELETED\].\[VALUE)\] INTO \[OUT\]/)
|
150
|
+
@ds.output(:out, {:name => Sequel[:inserted][:name], :value => Sequel[:deleted][:value]}).update_sql(:value => 2).must_match(/UPDATE \[ITEMS\] SET \[VALUE\] = 2 OUTPUT \[(INSERTED\].\[NAME|DELETED\].\[VALUE)\], \[(INSERTED\].\[NAME|DELETED\].\[VALUE)\] INTO \[OUT\] \(\[(NAME|VALUE)\], \[(NAME|VALUE)\]\)/)
|
151
151
|
end
|
152
152
|
|
153
153
|
it "should execute OUTPUT clauses in DELETE statements" do
|
154
154
|
@ds.insert(:name => "name", :value => 1)
|
155
|
-
@ds.output(:out, [:
|
155
|
+
@ds.output(:out, [Sequel[:deleted][:name], Sequel[:deleted][:value]]).delete
|
156
156
|
@db[:out].all.must_equal [{:name => "name", :value => 1}]
|
157
157
|
@ds.insert(:name => "name", :value => 2)
|
158
|
-
@ds.output(:out, {:name => :
|
158
|
+
@ds.output(:out, {:name => Sequel[:deleted][:name], :value => Sequel[:deleted][:value]}).delete
|
159
159
|
@db[:out].all.must_equal [{:name => "name", :value => 1}, {:name => "name", :value => 2}]
|
160
160
|
end
|
161
161
|
|
162
162
|
it "should execute OUTPUT clauses in INSERT statements" do
|
163
|
-
@ds.output(:out, [:
|
163
|
+
@ds.output(:out, [Sequel[:inserted][:name], Sequel[:inserted][:value]]).insert(:name => "name", :value => 1)
|
164
164
|
@db[:out].all.must_equal [{:name => "name", :value => 1}]
|
165
|
-
@ds.output(:out, {:name => :
|
165
|
+
@ds.output(:out, {:name => Sequel[:inserted][:name], :value => Sequel[:inserted][:value]}).insert(:name => "name", :value => 2)
|
166
166
|
@db[:out].all.must_equal [{:name => "name", :value => 1}, {:name => "name", :value => 2}]
|
167
167
|
end
|
168
168
|
|
169
169
|
it "should execute OUTPUT clauses in UPDATE statements" do
|
170
170
|
@ds.insert(:name => "name", :value => 1)
|
171
|
-
@ds.output(:out, [:
|
171
|
+
@ds.output(:out, [Sequel[:inserted][:name], Sequel[:deleted][:value]]).update(:value => 2)
|
172
172
|
@db[:out].all.must_equal [{:name => "name", :value => 1}]
|
173
|
-
@ds.output(:out, {:name => :
|
173
|
+
@ds.output(:out, {:name => Sequel[:inserted][:name], :value => Sequel[:deleted][:value]}).update(:value => 3)
|
174
174
|
@db[:out].all.must_equal [{:name => "name", :value => 1}, {:name => "name", :value => 2}]
|
175
175
|
end
|
176
176
|
end
|
@@ -216,7 +216,7 @@ describe "MSSQL::Dataset#import" do
|
|
216
216
|
|
217
217
|
it "#import should work correctly with an arbitrary output value" do
|
218
218
|
@db.create_table!(:test){primary_key :x; Integer :y}
|
219
|
-
@ds.output(nil, [:
|
219
|
+
@ds.output(nil, [Sequel[:inserted][:y], Sequel[:inserted][:x]]).import([:y], [[3], [4]]).must_equal [{:y=>3, :x=>1}, {:y=>4, :x=>2}]
|
220
220
|
@ds.all.must_equal [{:x=>1, :y=>3}, {:x=>2, :y=>4}]
|
221
221
|
end
|
222
222
|
|
@@ -233,11 +233,11 @@ describe "MSSQL joined datasets" do
|
|
233
233
|
end
|
234
234
|
|
235
235
|
it "should format DELETE statements" do
|
236
|
-
@db[:t1].inner_join(:t2, :
|
236
|
+
@db[:t1].inner_join(:t2, Sequel[:t1][:pk] => Sequel[:t2][:pk]).delete_sql.must_equal "DELETE FROM [T1] FROM [T1] INNER JOIN [T2] ON ([T1].[PK] = [T2].[PK])"
|
237
237
|
end
|
238
238
|
|
239
239
|
it "should format UPDATE statements" do
|
240
|
-
@db[:t1].inner_join(:t2, :
|
240
|
+
@db[:t1].inner_join(:t2, Sequel[:t1][:pk] => Sequel[:t2][:pk]).update_sql(:pk => Sequel[:t2][:pk]).must_equal "UPDATE [T1] SET [PK] = [T2].[PK] FROM [T1] INNER JOIN [T2] ON ([T1].[PK] = [T2].[PK])"
|
241
241
|
end
|
242
242
|
end
|
243
243
|
|
@@ -281,7 +281,7 @@ describe "Common Table Expressions" do
|
|
281
281
|
@ds.insert(:id=>1)
|
282
282
|
@ds2.insert(:id=>2, :parent_id=>1)
|
283
283
|
@ds2.insert(:id=>3, :parent_id=>2)
|
284
|
-
@ds.with(:t, @ds2).filter(:id => @db[:t].select(:id)).update(:parent_id => @db[:t].filter(:id => :
|
284
|
+
@ds.with(:t, @ds2).filter(:id => @db[:t].select(:id)).update(:parent_id => @db[:t].filter(:id => Sequel[:i1][:id]).select(:parent_id).limit(1))
|
285
285
|
@ds[:id => 1].must_equal(:id => 1, :parent_id => nil)
|
286
286
|
@ds[:id => 2].must_equal(:id => 2, :parent_id => 1)
|
287
287
|
@ds[:id => 3].must_equal(:id => 3, :parent_id => 2)
|
@@ -289,7 +289,7 @@ describe "Common Table Expressions" do
|
|
289
289
|
end
|
290
290
|
|
291
291
|
it "using #with_recursive should be able to update" do
|
292
|
-
ds = @ds.with_recursive(:t, @ds.filter(:parent_id=>1).or(:id => 1), @ds.join(:t, :i=>:parent_id).select(:
|
292
|
+
ds = @ds.with_recursive(:t, @ds.filter(:parent_id=>1).or(:id => 1), @ds.join(:t, :i=>:parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]), :args=>[:i, :pi])
|
293
293
|
ds.exclude(:id => @db[:t].select(:i)).update(:parent_id => 1)
|
294
294
|
@ds[:id => 1].must_equal(:id => 1, :parent_id => nil)
|
295
295
|
@ds[:id => 2].must_equal(:id => 2, :parent_id => 1)
|
@@ -303,7 +303,7 @@ describe "Common Table Expressions" do
|
|
303
303
|
end
|
304
304
|
|
305
305
|
it "using #with_recursive should be able to insert" do
|
306
|
-
ds = @ds2.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(:
|
306
|
+
ds = @ds2.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]), :args=>[:i, :pi])
|
307
307
|
ds.insert @db[:t]
|
308
308
|
@ds2.all.must_equal [{:id => 3, :parent_id => 1}, {:id => 4, :parent_id => 1}, {:id => 5, :parent_id => 3}, {:id => 6, :parent_id => 5}]
|
309
309
|
end
|
@@ -318,8 +318,8 @@ describe "Common Table Expressions" do
|
|
318
318
|
|
319
319
|
it "using #with_recursive should be able to delete" do
|
320
320
|
@ds.insert(:id=>7, :parent_id=>2)
|
321
|
-
ds = @ds.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(:
|
322
|
-
ds.filter(:
|
321
|
+
ds = @ds.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]), :args=>[:i, :pi])
|
322
|
+
ds.filter(Sequel[:i1][:id] => @db[:t].select(:i)).delete
|
323
323
|
@ds.all.must_equal [{:id => 1, :parent_id => nil}, {:id => 2, :parent_id => nil}, {:id => 7, :parent_id => 2}]
|
324
324
|
end
|
325
325
|
|
@@ -330,7 +330,7 @@ describe "Common Table Expressions" do
|
|
330
330
|
end
|
331
331
|
|
332
332
|
it "using #with_recursive should be able to import" do
|
333
|
-
ds = @ds2.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(:
|
333
|
+
ds = @ds2.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]), :args=>[:i, :pi])
|
334
334
|
ds.import [:id, :parent_id], @db[:t].select(:i, :pi)
|
335
335
|
@ds2.all.must_equal [{:id => 3, :parent_id => 1}, {:id => 4, :parent_id => 1}, {:id => 5, :parent_id => 3}, {:id => 6, :parent_id => 5}]
|
336
336
|
end
|
@@ -442,11 +442,11 @@ describe "MSSQL::Database#rename_table" do
|
|
442
442
|
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'MY')
|
443
443
|
EXECUTE sp_executesql N'create schema MY'
|
444
444
|
SQL
|
445
|
-
DB.create_table! :
|
445
|
+
DB.create_table! Sequel[:MY][:foo] do
|
446
446
|
text :name
|
447
447
|
end
|
448
|
-
DB.rename_table :
|
449
|
-
DB.rename_table :
|
448
|
+
DB.rename_table Sequel[:MY][:foo], Sequel[:MY][:bar]
|
449
|
+
DB.rename_table Sequel[:MY][:bar], :foo
|
450
450
|
end
|
451
451
|
end
|
452
452
|
|
@@ -550,14 +550,14 @@ describe "MSSQL::Database#drop_column with a schema" do
|
|
550
550
|
DB.run "create schema test" rescue nil
|
551
551
|
end
|
552
552
|
after do
|
553
|
-
DB.drop_table(:
|
553
|
+
DB.drop_table(Sequel[:test][:items])
|
554
554
|
DB.run "drop schema test" rescue nil
|
555
555
|
end
|
556
556
|
|
557
557
|
it "drops columns with a default value" do
|
558
|
-
DB.create_table!(:
|
559
|
-
DB.drop_column(:
|
560
|
-
DB[:
|
558
|
+
DB.create_table!(Sequel[:test][:items]){ Integer :id; String :name, :default => 'widget' }
|
559
|
+
DB.drop_column(Sequel[:test][:items], :name)
|
560
|
+
DB[Sequel[:test][:items]].columns.must_equal [:id]
|
561
561
|
end
|
562
562
|
end
|
563
563
|
|
@@ -606,24 +606,24 @@ describe "Database#foreign_key_list" do
|
|
606
606
|
describe "with multiple schemas" do
|
607
607
|
before(:all) do
|
608
608
|
DB.execute_ddl "create schema vendor"
|
609
|
-
DB.create_table! :
|
609
|
+
DB.create_table! Sequel[:vendor][:vendors] do
|
610
610
|
primary_key :id
|
611
611
|
varchar :name
|
612
612
|
end
|
613
|
-
DB.create_table! :
|
613
|
+
DB.create_table! Sequel[:vendor][:mapping] do
|
614
614
|
integer :vendor_id
|
615
615
|
integer :item_id
|
616
|
-
foreign_key [:vendor_id], :
|
616
|
+
foreign_key [:vendor_id], Sequel[:vendor][:vendors], :name => :fk_mapping_vendor
|
617
617
|
foreign_key [:item_id], :items, :name => :fk_mapping_item
|
618
618
|
end
|
619
619
|
end
|
620
620
|
after(:all) do
|
621
|
-
DB.drop_table? :
|
622
|
-
DB.drop_table? :
|
621
|
+
DB.drop_table? Sequel[:vendor][:mapping]
|
622
|
+
DB.drop_table? Sequel[:vendor][:vendors]
|
623
623
|
DB.execute_ddl "drop schema vendor"
|
624
624
|
end
|
625
625
|
it "should support mixed schema bound tables" do
|
626
|
-
DB.foreign_key_list(:
|
626
|
+
DB.foreign_key_list(Sequel[:vendor][:mapping]).sort_by{|h| h[:name].to_s}.must_equal [{:name => :fk_mapping_item, :table => :items, :columns => [:item_id], :key => [:id], :on_update => :no_action, :on_delete => :no_action }, {:name => :fk_mapping_vendor, :table => Sequel.qualify(:vendor, :vendors), :columns => [:vendor_id], :key => [:id], :on_update => :no_action, :on_delete => :no_action }]
|
627
627
|
end
|
628
628
|
end
|
629
629
|
end
|