sequel 4.9.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +79 -1
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/Rakefile +2 -12
- data/bin/sequel +1 -0
- data/doc/advanced_associations.rdoc +82 -25
- data/doc/association_basics.rdoc +21 -22
- data/doc/core_extensions.rdoc +1 -1
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/4.10.0.txt +226 -0
- data/doc/security.rdoc +1 -0
- data/doc/testing.rdoc +7 -7
- data/doc/transactions.rdoc +8 -0
- data/lib/sequel/adapters/jdbc.rb +160 -168
- data/lib/sequel/adapters/jdbc/db2.rb +17 -18
- data/lib/sequel/adapters/jdbc/derby.rb +5 -28
- data/lib/sequel/adapters/jdbc/h2.rb +11 -22
- data/lib/sequel/adapters/jdbc/hsqldb.rb +31 -18
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -15
- data/lib/sequel/adapters/jdbc/oracle.rb +36 -35
- data/lib/sequel/adapters/jdbc/postgresql.rb +72 -90
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +18 -16
- data/lib/sequel/adapters/jdbc/sqlite.rb +7 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -30
- data/lib/sequel/adapters/jdbc/transactions.rb +5 -6
- data/lib/sequel/adapters/openbase.rb +1 -7
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +3 -6
- data/lib/sequel/adapters/shared/cubrid.rb +24 -9
- data/lib/sequel/adapters/shared/db2.rb +13 -5
- data/lib/sequel/adapters/shared/firebird.rb +16 -16
- data/lib/sequel/adapters/shared/informix.rb +2 -5
- data/lib/sequel/adapters/shared/mssql.rb +72 -63
- data/lib/sequel/adapters/shared/mysql.rb +72 -40
- data/lib/sequel/adapters/shared/oracle.rb +27 -15
- data/lib/sequel/adapters/shared/postgres.rb +24 -44
- data/lib/sequel/adapters/shared/progress.rb +1 -5
- data/lib/sequel/adapters/shared/sqlanywhere.rb +26 -18
- data/lib/sequel/adapters/shared/sqlite.rb +21 -6
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
- data/lib/sequel/adapters/utils/split_alter_table.rb +8 -0
- data/lib/sequel/core.rb +14 -9
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +12 -0
- data/lib/sequel/database/query.rb +4 -1
- data/lib/sequel/database/schema_methods.rb +3 -2
- data/lib/sequel/database/transactions.rb +47 -17
- data/lib/sequel/dataset/features.rb +12 -2
- data/lib/sequel/dataset/mutation.rb +2 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +12 -4
- data/lib/sequel/dataset/prepared_statements.rb +6 -0
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/dataset/sql.rb +132 -70
- data/lib/sequel/extensions/columns_introspection.rb +1 -1
- data/lib/sequel/extensions/null_dataset.rb +8 -4
- data/lib/sequel/extensions/pg_array.rb +4 -4
- data/lib/sequel/extensions/pg_row.rb +1 -0
- data/lib/sequel/model/associations.rb +468 -188
- data/lib/sequel/model/base.rb +88 -13
- data/lib/sequel/plugins/association_pks.rb +23 -64
- data/lib/sequel/plugins/auto_validations.rb +3 -2
- data/lib/sequel/plugins/dataset_associations.rb +1 -3
- data/lib/sequel/plugins/many_through_many.rb +18 -65
- data/lib/sequel/plugins/pg_array_associations.rb +97 -86
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +36 -27
- data/lib/sequel/plugins/rcte_tree.rb +12 -16
- data/lib/sequel/plugins/sharding.rb +21 -3
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -1
- data/lib/sequel/plugins/subclasses.rb +1 -9
- data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
- data/lib/sequel/plugins/tree.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +57 -15
- data/spec/adapters/mysql_spec.rb +11 -0
- data/spec/bin_spec.rb +2 -2
- data/spec/core/database_spec.rb +38 -4
- data/spec/core/dataset_spec.rb +45 -7
- data/spec/core/placeholder_literalizer_spec.rb +17 -0
- data/spec/core/schema_spec.rb +6 -1
- data/spec/extensions/active_model_spec.rb +18 -9
- data/spec/extensions/association_pks_spec.rb +20 -18
- data/spec/extensions/association_proxies_spec.rb +9 -9
- data/spec/extensions/auto_validations_spec.rb +6 -0
- data/spec/extensions/columns_introspection_spec.rb +1 -0
- data/spec/extensions/constraint_validations_spec.rb +3 -1
- data/spec/extensions/many_through_many_spec.rb +191 -111
- data/spec/extensions/pg_array_associations_spec.rb +133 -103
- data/spec/extensions/prepared_statements_associations_spec.rb +23 -4
- data/spec/extensions/rcte_tree_spec.rb +35 -27
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -1
- data/spec/extensions/sharding_spec.rb +2 -2
- data/spec/extensions/tactical_eager_loading_spec.rb +4 -0
- data/spec/extensions/to_dot_spec.rb +1 -0
- data/spec/extensions/touch_spec.rb +2 -2
- data/spec/integration/associations_test.rb +130 -37
- data/spec/integration/dataset_test.rb +17 -0
- data/spec/integration/model_test.rb +17 -0
- data/spec/integration/schema_test.rb +14 -0
- data/spec/integration/transaction_test.rb +25 -1
- data/spec/model/association_reflection_spec.rb +63 -24
- data/spec/model/associations_spec.rb +104 -57
- data/spec/model/base_spec.rb +14 -1
- data/spec/model/class_dataset_methods_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +221 -74
- data/spec/model/model_spec.rb +119 -1
- metadata +4 -2
@@ -3,6 +3,14 @@ Sequel.require 'adapters/jdbc/transactions'
|
|
3
3
|
|
4
4
|
module Sequel
|
5
5
|
module JDBC
|
6
|
+
class TypeConvertor
|
7
|
+
def SqlAnywhereBoolean(r, i)
|
8
|
+
if v = Short(r, i)
|
9
|
+
v != 0
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
6
14
|
module SqlAnywhere
|
7
15
|
# Database instance methods for Sybase databases accessed via JDBC.
|
8
16
|
module DatabaseMethods
|
@@ -23,6 +31,11 @@ module Sequel
|
|
23
31
|
rs.getInt(1)
|
24
32
|
end
|
25
33
|
end
|
34
|
+
|
35
|
+
def setup_type_convertor_map
|
36
|
+
super
|
37
|
+
@type_convertor_map[:SqlAnywhereBoolean] = TypeConvertor::INSTANCE.method(:SqlAnywhereBoolean)
|
38
|
+
end
|
26
39
|
end
|
27
40
|
|
28
41
|
#Dataset class for Sybase datasets accessed via JDBC.
|
@@ -31,26 +44,15 @@ module Sequel
|
|
31
44
|
|
32
45
|
private
|
33
46
|
|
34
|
-
|
35
|
-
def sqla_boolean(i) i != 0 end
|
36
|
-
end
|
37
|
-
|
38
|
-
BOOLEAN_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:sqla_boolean)
|
47
|
+
SMALLINT_TYPE = Java::JavaSQL::Types::SMALLINT
|
39
48
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
BOOLEAN_METHOD
|
49
|
+
def type_convertor(map, meta, type, i)
|
50
|
+
if convert_smallint_to_bool && type == SMALLINT_TYPE
|
51
|
+
map[:SqlAnywhereBoolean]
|
44
52
|
else
|
45
|
-
super
|
53
|
+
super
|
46
54
|
end
|
47
55
|
end
|
48
|
-
|
49
|
-
# SQLAnywhere needs the column info if it is converting smallint to bool,
|
50
|
-
# since the JDBC adapter always returns smallint as integer.
|
51
|
-
def convert_type_proc_uses_column_info?
|
52
|
-
convert_smallint_to_bool
|
53
|
-
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
@@ -59,6 +59,13 @@ module Sequel
|
|
59
59
|
end
|
60
60
|
conn
|
61
61
|
end
|
62
|
+
|
63
|
+
# Use getLong instead of getInt for converting integers on SQLite, since SQLite does not enforce a limit of 2**32.
|
64
|
+
def setup_type_convertor_map
|
65
|
+
super
|
66
|
+
@type_convertor_map[Java::JavaSQL::Types::INTEGER] = @type_convertor_map[Java::JavaSQL::Types::BIGINT]
|
67
|
+
@basic_type_convertor_map[Java::JavaSQL::Types::INTEGER] = @basic_type_convertor_map[Java::JavaSQL::Types::BIGINT]
|
68
|
+
end
|
62
69
|
end
|
63
70
|
end
|
64
71
|
end
|
@@ -19,39 +19,19 @@ module Sequel
|
|
19
19
|
# than getObject() for this column avoids the problem.
|
20
20
|
# Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
|
21
21
|
module MetadataDatasetMethods
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
row[n] = if v
|
28
|
-
if p
|
29
|
-
p.call(v)
|
30
|
-
elsif p.nil?
|
31
|
-
cols[i-1][3] = p = convert_type_proc(v, ctn)
|
32
|
-
if p
|
33
|
-
p.call(v)
|
34
|
-
else
|
35
|
-
v
|
36
|
-
end
|
37
|
-
else
|
38
|
-
v
|
39
|
-
end
|
40
|
-
else
|
41
|
-
v
|
42
|
-
end
|
43
|
-
end
|
44
|
-
yield row
|
22
|
+
def type_convertor(map, meta, type, i)
|
23
|
+
if output_identifier(meta.getColumnLabel(i)) == :is_autoincrement
|
24
|
+
map[Java::JavaSQL::Types::VARCHAR]
|
25
|
+
else
|
26
|
+
super
|
45
27
|
end
|
46
28
|
end
|
47
29
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
yield row
|
30
|
+
def basic_type_convertor(map, meta, type, i)
|
31
|
+
if output_identifier(meta.getColumnLabel(i)) == :is_autoincrement
|
32
|
+
map[Java::JavaSQL::Types::VARCHAR]
|
33
|
+
else
|
34
|
+
super
|
55
35
|
end
|
56
36
|
end
|
57
37
|
end
|
@@ -47,14 +47,13 @@ module Sequel
|
|
47
47
|
def begin_transaction(conn, opts=OPTS)
|
48
48
|
if supports_savepoints?
|
49
49
|
th = _trans(conn)
|
50
|
-
if sps = th[:
|
50
|
+
if sps = th[:savepoint_objs]
|
51
51
|
sps << log_yield(TRANSACTION_SAVEPOINT){conn.set_savepoint}
|
52
52
|
else
|
53
53
|
log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
|
54
|
-
th[:
|
54
|
+
th[:savepoint_objs] = []
|
55
55
|
set_transaction_isolation(conn, opts)
|
56
56
|
end
|
57
|
-
th[:savepoint_level] += 1
|
58
57
|
else
|
59
58
|
log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
|
60
59
|
set_transaction_isolation(conn, opts)
|
@@ -64,7 +63,7 @@ module Sequel
|
|
64
63
|
# Use JDBC connection's commit method to commit transactions
|
65
64
|
def commit_transaction(conn, opts=OPTS)
|
66
65
|
if supports_savepoints?
|
67
|
-
sps = _trans(conn)[:
|
66
|
+
sps = _trans(conn)[:savepoint_objs]
|
68
67
|
if sps.empty?
|
69
68
|
log_yield(TRANSACTION_COMMIT){conn.commit}
|
70
69
|
elsif supports_releasing_savepoints?
|
@@ -81,7 +80,7 @@ module Sequel
|
|
81
80
|
conn.setTransactionIsolation(jdbc_level)
|
82
81
|
end
|
83
82
|
if supports_savepoints?
|
84
|
-
sps = _trans(conn)[:
|
83
|
+
sps = _trans(conn)[:savepoint_objs]
|
85
84
|
conn.setAutoCommit(true) if sps.empty?
|
86
85
|
sps.pop
|
87
86
|
else
|
@@ -94,7 +93,7 @@ module Sequel
|
|
94
93
|
# Use JDBC connection's rollback method to rollback transactions
|
95
94
|
def rollback_transaction(conn, opts=OPTS)
|
96
95
|
if supports_savepoints?
|
97
|
-
sps = _trans(conn)[:
|
96
|
+
sps = _trans(conn)[:savepoint_objs]
|
98
97
|
if sps.empty?
|
99
98
|
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
100
99
|
else
|
@@ -29,7 +29,7 @@ module Sequel
|
|
29
29
|
end
|
30
30
|
|
31
31
|
class Dataset < Sequel::Dataset
|
32
|
-
|
32
|
+
def_sql_method(self, :select, %w'select distinct columns from join where group having compounds order limit')
|
33
33
|
|
34
34
|
Database::DatasetClass = self
|
35
35
|
|
@@ -48,12 +48,6 @@ module Sequel
|
|
48
48
|
end
|
49
49
|
self
|
50
50
|
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def select_clause_methods
|
55
|
-
SELECT_CLAUSE_METHODS
|
56
|
-
end
|
57
51
|
end
|
58
52
|
end
|
59
53
|
end
|
@@ -108,7 +108,7 @@ module Sequel
|
|
108
108
|
# PGconn subclass for connection specific methods used with the
|
109
109
|
# pg, postgres, or postgres-pr driver.
|
110
110
|
class Adapter < ::PGconn
|
111
|
-
DISCONNECT_ERROR_RE = /\
|
111
|
+
DISCONNECT_ERROR_RE = /\A(?:could not receive data from server|no connection to the server|connection not open)/
|
112
112
|
|
113
113
|
self.translate_results = false if respond_to?(:translate_results=)
|
114
114
|
|
@@ -88,9 +88,11 @@ module Sequel
|
|
88
88
|
end
|
89
89
|
|
90
90
|
module DatasetMethods
|
91
|
+
include(Module.new do
|
92
|
+
Dataset.def_sql_method(self, :select, %w'select distinct limit columns into from join where group order having compounds')
|
93
|
+
end)
|
91
94
|
include EmulateOffsetWithReverseAndCount
|
92
95
|
|
93
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct limit columns into from join where group order having compounds')
|
94
96
|
DATE_FORMAT = '#%Y-%m-%d#'.freeze
|
95
97
|
TIMESTAMP_FORMAT = '#%Y-%m-%d %H:%M:%S#'.freeze
|
96
98
|
TOP = " TOP ".freeze
|
@@ -295,11 +297,6 @@ module Sequel
|
|
295
297
|
def quoted_identifier_append(sql, v)
|
296
298
|
sql << BRACKET_OPEN << v.to_s << BRACKET_CLOSE
|
297
299
|
end
|
298
|
-
|
299
|
-
# Access requires the limit clause come before other clauses
|
300
|
-
def select_clause_methods
|
301
|
-
SELECT_CLAUSE_METHODS
|
302
|
-
end
|
303
300
|
end
|
304
301
|
end
|
305
302
|
end
|
@@ -165,12 +165,14 @@ module Sequel
|
|
165
165
|
end
|
166
166
|
|
167
167
|
module DatasetMethods
|
168
|
-
SELECT_CLAUSE_METHODS = Sequel::Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
|
169
|
-
LIMIT = Sequel::Dataset::LIMIT
|
170
168
|
COMMA = Sequel::Dataset::COMMA
|
169
|
+
LIMIT = Sequel::Dataset::LIMIT
|
171
170
|
BOOL_FALSE = '0'.freeze
|
172
171
|
BOOL_TRUE = '1'.freeze
|
173
172
|
|
173
|
+
# Hope you don't have more than 2**32 + offset rows in your dataset
|
174
|
+
ONLY_OFFSET = ",4294967295".freeze
|
175
|
+
|
174
176
|
def supports_join_using?
|
175
177
|
false
|
176
178
|
end
|
@@ -200,23 +202,36 @@ module Sequel
|
|
200
202
|
BOOL_TRUE
|
201
203
|
end
|
202
204
|
|
203
|
-
# CUBRID
|
204
|
-
def
|
205
|
-
|
205
|
+
# CUBRID supports multiple rows in INSERT.
|
206
|
+
def multi_insert_sql_strategy
|
207
|
+
:values
|
206
208
|
end
|
207
209
|
|
208
210
|
# CUBRID requires a limit to use an offset,
|
209
211
|
# and requires a FROM table if a limit is used.
|
210
212
|
def select_limit_sql(sql)
|
211
|
-
|
213
|
+
return unless @opts[:from]
|
214
|
+
l = @opts[:limit]
|
215
|
+
o = @opts[:offset]
|
216
|
+
if l || o
|
212
217
|
sql << LIMIT
|
213
|
-
if o
|
218
|
+
if o
|
214
219
|
literal_append(sql, o)
|
215
|
-
|
220
|
+
if l
|
221
|
+
sql << COMMA
|
222
|
+
literal_append(sql, l)
|
223
|
+
else
|
224
|
+
sql << ONLY_OFFSET
|
225
|
+
end
|
226
|
+
else
|
227
|
+
literal_append(sql, l)
|
216
228
|
end
|
217
|
-
literal_append(sql, l)
|
218
229
|
end
|
219
230
|
end
|
231
|
+
|
232
|
+
# CUBRID doesn't support FOR UPDATE.
|
233
|
+
def select_lock_sql(sql)
|
234
|
+
end
|
220
235
|
end
|
221
236
|
end
|
222
237
|
end
|
@@ -270,6 +270,10 @@ module Sequel
|
|
270
270
|
end
|
271
271
|
end
|
272
272
|
|
273
|
+
def supports_cte?(type=:select)
|
274
|
+
type == :select
|
275
|
+
end
|
276
|
+
|
273
277
|
# DB2 supports GROUP BY CUBE
|
274
278
|
def supports_group_cube?
|
275
279
|
true
|
@@ -317,6 +321,10 @@ module Sequel
|
|
317
321
|
|
318
322
|
private
|
319
323
|
|
324
|
+
def empty_from_sql
|
325
|
+
EMPTY_FROM_TABLE
|
326
|
+
end
|
327
|
+
|
320
328
|
# DB2 needs the standard workaround to insert all default values into
|
321
329
|
# a table with more than one column.
|
322
330
|
def insert_supports_empty_values?
|
@@ -342,16 +350,16 @@ module Sequel
|
|
342
350
|
end
|
343
351
|
end
|
344
352
|
|
353
|
+
# DB2 can insert multiple rows using a UNION
|
354
|
+
def multi_insert_sql_strategy
|
355
|
+
:union
|
356
|
+
end
|
357
|
+
|
345
358
|
# DB2 does not require that ROW_NUMBER be ordered.
|
346
359
|
def require_offset_order?
|
347
360
|
false
|
348
361
|
end
|
349
362
|
|
350
|
-
# Add a fallback table for empty from situation
|
351
|
-
def select_from_sql(sql)
|
352
|
-
@opts[:from] ? super : (sql << EMPTY_FROM_TABLE)
|
353
|
-
end
|
354
|
-
|
355
363
|
# Modify the sql to limit the number of rows returned
|
356
364
|
# Note:
|
357
365
|
#
|
@@ -152,12 +152,13 @@ module Sequel
|
|
152
152
|
BOOL_TRUE = '1'.freeze
|
153
153
|
BOOL_FALSE = '0'.freeze
|
154
154
|
NULL = LiteralString.new('NULL').freeze
|
155
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns from join where group having compounds order')
|
156
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
|
157
155
|
FIRST = " FIRST ".freeze
|
158
156
|
SKIP = " SKIP ".freeze
|
159
157
|
DEFAULT_FROM = " FROM RDB$DATABASE"
|
160
158
|
|
159
|
+
Dataset.def_sql_method(self, :select, %w'with select distinct limit columns from join where group having compounds order')
|
160
|
+
Dataset.def_sql_method(self, :insert, %w'insert into columns values returning')
|
161
|
+
|
161
162
|
# Insert given values into the database.
|
162
163
|
def insert(*values)
|
163
164
|
if @opts[:sql] || @opts[:returning]
|
@@ -176,6 +177,10 @@ module Sequel
|
|
176
177
|
true
|
177
178
|
end
|
178
179
|
|
180
|
+
def supports_cte?(type=:select)
|
181
|
+
type == :select
|
182
|
+
end
|
183
|
+
|
179
184
|
def supports_insert_select?
|
180
185
|
true
|
181
186
|
end
|
@@ -185,10 +190,14 @@ module Sequel
|
|
185
190
|
false
|
186
191
|
end
|
187
192
|
|
193
|
+
def supports_returning?(type)
|
194
|
+
type == :insert
|
195
|
+
end
|
196
|
+
|
188
197
|
private
|
189
198
|
|
190
|
-
def
|
191
|
-
|
199
|
+
def empty_from_sql
|
200
|
+
DEFAULT_FROM
|
192
201
|
end
|
193
202
|
|
194
203
|
def insert_pk(*values)
|
@@ -204,19 +213,10 @@ module Sequel
|
|
204
213
|
BOOL_TRUE
|
205
214
|
end
|
206
215
|
|
207
|
-
#
|
208
|
-
def
|
209
|
-
|
216
|
+
# Firebird can insert multiple rows using a UNION
|
217
|
+
def multi_insert_sql_strategy
|
218
|
+
:union
|
210
219
|
end
|
211
|
-
|
212
|
-
# Use a default FROM table if the dataset does not contain a FROM table.
|
213
|
-
def select_from_sql(sql)
|
214
|
-
if @opts[:from]
|
215
|
-
super
|
216
|
-
else
|
217
|
-
sql << DEFAULT_FROM
|
218
|
-
end
|
219
|
-
end
|
220
220
|
|
221
221
|
def select_limit_sql(sql)
|
222
222
|
if l = @opts[:limit]
|
@@ -25,20 +25,17 @@ module Sequel
|
|
25
25
|
end
|
26
26
|
|
27
27
|
module DatasetMethods
|
28
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where having group compounds order')
|
29
28
|
FIRST = " FIRST ".freeze
|
30
29
|
SKIP = " SKIP ".freeze
|
31
30
|
|
32
|
-
|
31
|
+
Dataset.def_sql_method(self, :select, %w'select limit distinct columns from join where having group compounds order')
|
33
32
|
|
34
33
|
# Informix does not support INTERSECT or EXCEPT
|
35
34
|
def supports_intersect_except?
|
36
35
|
false
|
37
36
|
end
|
38
37
|
|
39
|
-
|
40
|
-
SELECT_CLAUSE_METHODS
|
41
|
-
end
|
38
|
+
private
|
42
39
|
|
43
40
|
def select_limit_sql(sql)
|
44
41
|
if o = @opts[:offset]
|
@@ -37,39 +37,73 @@ module Sequel
|
|
37
37
|
# Execute the given stored procedure with the given name.
|
38
38
|
#
|
39
39
|
# Options:
|
40
|
-
# :args ::
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
40
|
+
# :args :: Arguments to stored procedure. For named argumetns, this should be a
|
41
|
+
# hash keyed by argument named. For unnamed arguments, this should be an
|
42
|
+
# array. Output parameters to the function are specified using :output.
|
43
|
+
# You can also name output parameters and provide a type by using an
|
44
|
+
# array containing :output, the type name, and the parameter name.
|
44
45
|
# :server :: The server/shard on which to execute the procedure.
|
45
46
|
#
|
47
|
+
# This method returns a single hash with the following keys:
|
48
|
+
#
|
49
|
+
# :result :: The result code of the stored procedure
|
50
|
+
# :numrows :: The number of rows affected by the stored procedure
|
51
|
+
# output params :: Values for any output paramters, using the name given for the output parameter
|
52
|
+
#
|
46
53
|
# Examples:
|
47
54
|
#
|
48
55
|
# DB.call_mssql_sproc(:SequelTest, {:args => ['input arg', :output]})
|
49
56
|
# DB.call_mssql_sproc(:SequelTest, {:args => ['input arg', [:output, 'int', 'varname']]})
|
57
|
+
#
|
58
|
+
# named params:
|
59
|
+
# DB.call_mssql_sproc(:SequelTest, :args => {
|
60
|
+
# 'input_arg1_name' => 'input arg1 value',
|
61
|
+
# 'input_arg2_name' => 'input arg2 value',
|
62
|
+
# 'output_arg_name' => [:output, 'int', 'varname']
|
63
|
+
# })
|
50
64
|
def call_mssql_sproc(name, opts=OPTS)
|
51
65
|
args = opts[:args] || []
|
52
66
|
names = ['@RC AS RESULT', '@@ROWCOUNT AS NUMROWS']
|
53
67
|
declarations = ['@RC int']
|
54
68
|
values = []
|
55
69
|
|
56
|
-
args.
|
57
|
-
|
58
|
-
|
70
|
+
if args.is_a?(Hash)
|
71
|
+
named_args = true
|
72
|
+
args = args.to_a
|
73
|
+
method = :each
|
74
|
+
else
|
75
|
+
method = :each_with_index
|
76
|
+
end
|
77
|
+
|
78
|
+
args.send(method) do |v, i|
|
79
|
+
if named_args
|
80
|
+
k = v
|
81
|
+
v, type, select = i
|
82
|
+
raise Error, "must provide output parameter name when using output parameters with named arguments" if v == :output && !select
|
59
83
|
else
|
60
|
-
type
|
84
|
+
v, type, select = v
|
61
85
|
end
|
62
86
|
|
63
87
|
if v == :output
|
64
|
-
type
|
65
|
-
|
66
|
-
|
88
|
+
type ||= "nvarchar(max)"
|
89
|
+
if named_args
|
90
|
+
varname = select
|
91
|
+
else
|
92
|
+
varname = "var#{i}"
|
93
|
+
select ||= varname
|
94
|
+
end
|
67
95
|
names << "@#{varname} AS #{quote_identifier(select)}"
|
68
96
|
declarations << "@#{varname} #{type}"
|
69
|
-
|
97
|
+
value = "@#{varname} OUTPUT"
|
70
98
|
else
|
71
|
-
|
99
|
+
value = literal(v)
|
100
|
+
end
|
101
|
+
|
102
|
+
if named_args
|
103
|
+
value = "@#{k}=#{value}"
|
72
104
|
end
|
105
|
+
|
106
|
+
values << value
|
73
107
|
end
|
74
108
|
|
75
109
|
sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
|
@@ -159,6 +193,9 @@ module Sequel
|
|
159
193
|
# SQL Server 2008 Express).
|
160
194
|
def server_version(server=nil)
|
161
195
|
return @server_version if @server_version
|
196
|
+
if @opts[:server_version]
|
197
|
+
return @server_version = Integer(@opts[:server_version])
|
198
|
+
end
|
162
199
|
@server_version = synchronize(server) do |conn|
|
163
200
|
(conn.server_version rescue nil) if conn.respond_to?(:server_version)
|
164
201
|
end
|
@@ -276,7 +313,7 @@ module Sequel
|
|
276
313
|
# Commit the active transaction on the connection, does not commit/release
|
277
314
|
# savepoints.
|
278
315
|
def commit_transaction(conn, opts=OPTS)
|
279
|
-
log_connection_execute(conn, commit_transaction_sql) unless
|
316
|
+
log_connection_execute(conn, commit_transaction_sql) unless savepoint_level(conn) > 1
|
280
317
|
end
|
281
318
|
|
282
319
|
# SQL to COMMIT a transaction.
|
@@ -453,16 +490,14 @@ module Sequel
|
|
453
490
|
end
|
454
491
|
|
455
492
|
module DatasetMethods
|
493
|
+
include(Module.new do
|
494
|
+
Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from lock join where group having order compounds')
|
495
|
+
end)
|
456
496
|
include EmulateOffsetWithRowNumber
|
457
497
|
|
458
498
|
BOOL_TRUE = '1'.freeze
|
459
499
|
BOOL_FALSE = '0'.freeze
|
460
500
|
COMMA_SEPARATOR = ', '.freeze
|
461
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with delete from output from2 where')
|
462
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with insert into columns output values')
|
463
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns into from lock join where group having order compounds')
|
464
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with update limit table set output from where')
|
465
|
-
UPDATE_CLAUSE_METHODS_2000 = Dataset.clause_methods(:update, %w'update table set output from where')
|
466
501
|
NOLOCK = ' WITH (NOLOCK)'.freeze
|
467
502
|
UPDLOCK = ' WITH (UPDLOCK)'.freeze
|
468
503
|
WILDCARD = LiteralString.new('*').freeze
|
@@ -484,8 +519,6 @@ module Sequel
|
|
484
519
|
DATEPART_SECOND_MIDDLE = ') + datepart(ns, '.freeze
|
485
520
|
DATEPART_SECOND_CLOSE = ")/1000000000.0) AS double precision)".freeze
|
486
521
|
DATEPART_OPEN = "datepart(".freeze
|
487
|
-
UNION_ALL = ' UNION ALL '.freeze
|
488
|
-
SELECT_SPACE = 'SELECT '.freeze
|
489
522
|
TIMESTAMP_USEC_FORMAT = ".%03d".freeze
|
490
523
|
OUTPUT_INSERTED = " OUTPUT INSERTED.*".freeze
|
491
524
|
HEX_START = '0x'.freeze
|
@@ -507,7 +540,11 @@ module Sequel
|
|
507
540
|
ROWS_ONLY = " ROWS ONLY".freeze
|
508
541
|
FETCH_NEXT = " FETCH NEXT ".freeze
|
509
542
|
|
510
|
-
|
543
|
+
Dataset.def_mutation_method(:disable_insert_output, :output, :module=>self)
|
544
|
+
Dataset.def_sql_method(self, :delete, %w'with delete from output from2 where')
|
545
|
+
Dataset.def_sql_method(self, :insert, %w'with insert into columns output values')
|
546
|
+
Dataset.def_sql_method(self, :update, [['if is_2005_or_later?', %w'with update limit table set output from where'], ['else', %w'update table set output from where']])
|
547
|
+
|
511
548
|
|
512
549
|
# Allow overriding of the mssql_unicode_strings option at the dataset level.
|
513
550
|
attr_writer :mssql_unicode_strings
|
@@ -604,20 +641,6 @@ module Sequel
|
|
604
641
|
clone(:into => table)
|
605
642
|
end
|
606
643
|
|
607
|
-
# MSSQL uses a UNION ALL statement to insert multiple values at once.
|
608
|
-
def multi_insert_sql(columns, values)
|
609
|
-
c = false
|
610
|
-
sql = LiteralString.new('')
|
611
|
-
u = UNION_ALL
|
612
|
-
values.each do |v|
|
613
|
-
sql << u if c
|
614
|
-
sql << SELECT_SPACE
|
615
|
-
expression_list_append(sql, v)
|
616
|
-
c ||= true
|
617
|
-
end
|
618
|
-
[insert_sql(columns, sql)]
|
619
|
-
end
|
620
|
-
|
621
644
|
# Allows you to do a dirty read of uncommitted data using WITH (NOLOCK).
|
622
645
|
def nolock
|
623
646
|
lock_style(:dirty)
|
@@ -675,6 +698,10 @@ module Sequel
|
|
675
698
|
db.server_version(@opts[:server])
|
676
699
|
end
|
677
700
|
|
701
|
+
def supports_cte?(type=:select)
|
702
|
+
is_2005_or_later?
|
703
|
+
end
|
704
|
+
|
678
705
|
# MSSQL 2005+ supports GROUP BY CUBE.
|
679
706
|
def supports_group_cube?
|
680
707
|
is_2005_or_later?
|
@@ -715,6 +742,11 @@ module Sequel
|
|
715
742
|
false
|
716
743
|
end
|
717
744
|
|
745
|
+
# MSSQL 2012+ supports offsets in correlated subqueries.
|
746
|
+
def supports_offsets_in_correlated_subqueries?
|
747
|
+
is_2012_or_later?
|
748
|
+
end
|
749
|
+
|
718
750
|
# MSSQL 2005+ supports the output clause.
|
719
751
|
def supports_output_clause?
|
720
752
|
is_2005_or_later?
|
@@ -778,12 +810,6 @@ module Sequel
|
|
778
810
|
DEFAULT_TIMESTAMP_FORMAT
|
779
811
|
end
|
780
812
|
|
781
|
-
# MSSQL supports the OUTPUT clause for DELETE statements.
|
782
|
-
# It also allows prepending a WITH clause.
|
783
|
-
def delete_clause_methods
|
784
|
-
DELETE_CLAUSE_METHODS
|
785
|
-
end
|
786
|
-
|
787
813
|
# Only include the primary table in the main delete clause
|
788
814
|
def delete_from_sql(sql)
|
789
815
|
sql << FROM
|
@@ -817,12 +843,6 @@ module Sequel
|
|
817
843
|
sprintf(TIMESTAMP_USEC_FORMAT, usec/1000)
|
818
844
|
end
|
819
845
|
|
820
|
-
# MSSQL supports the OUTPUT clause for INSERT statements.
|
821
|
-
# It also allows prepending a WITH clause.
|
822
|
-
def insert_clause_methods
|
823
|
-
INSERT_CLAUSE_METHODS
|
824
|
-
end
|
825
|
-
|
826
846
|
# Use OUTPUT INSERTED.* to return all columns of the inserted row,
|
827
847
|
# for use with the prepared statement code.
|
828
848
|
def insert_output_sql(sql)
|
@@ -873,10 +893,10 @@ module Sequel
|
|
873
893
|
BOOL_TRUE
|
874
894
|
end
|
875
895
|
|
876
|
-
# MSSQL
|
877
|
-
#
|
878
|
-
def
|
879
|
-
|
896
|
+
# MSSQL 2008+ supports multiple rows in the VALUES clause, older versions
|
897
|
+
# can use UNION.
|
898
|
+
def multi_insert_sql_strategy
|
899
|
+
is_2008_or_later? ? :values : :union
|
880
900
|
end
|
881
901
|
|
882
902
|
def select_into_sql(sql)
|
@@ -954,17 +974,6 @@ module Sequel
|
|
954
974
|
alias delete_output_sql output_sql
|
955
975
|
alias update_output_sql output_sql
|
956
976
|
|
957
|
-
# MSSQL supports the OUTPUT and TOP clause for UPDATE statements.
|
958
|
-
# It also allows prepending a WITH clause. For MSSQL 2000
|
959
|
-
# and below, exclude WITH and TOP.
|
960
|
-
def update_clause_methods
|
961
|
-
if is_2005_or_later?
|
962
|
-
UPDATE_CLAUSE_METHODS
|
963
|
-
else
|
964
|
-
UPDATE_CLAUSE_METHODS_2000
|
965
|
-
end
|
966
|
-
end
|
967
|
-
|
968
977
|
# Only include the primary table in the main update clause
|
969
978
|
def update_table_sql(sql)
|
970
979
|
sql << SPACE
|