sequel 4.9.0 → 4.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|