sequel 3.29.0 → 3.30.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.
- data/CHANGELOG +35 -3
- data/Rakefile +2 -1
- data/doc/association_basics.rdoc +11 -0
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/testing.rdoc +17 -3
- data/lib/sequel/adapters/amalgalite.rb +2 -2
- data/lib/sequel/adapters/do/mysql.rb +5 -2
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +126 -43
- data/lib/sequel/adapters/jdbc/as400.rb +11 -3
- data/lib/sequel/adapters/jdbc/db2.rb +2 -1
- data/lib/sequel/adapters/jdbc/derby.rb +44 -19
- data/lib/sequel/adapters/jdbc/h2.rb +32 -19
- data/lib/sequel/adapters/jdbc/hsqldb.rb +21 -17
- data/lib/sequel/adapters/jdbc/jtds.rb +9 -4
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +21 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +48 -18
- data/lib/sequel/adapters/mock.rb +2 -1
- data/lib/sequel/adapters/mysql.rb +4 -2
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +6 -6
- data/lib/sequel/adapters/postgres.rb +25 -12
- data/lib/sequel/adapters/shared/access.rb +14 -6
- data/lib/sequel/adapters/shared/db2.rb +36 -13
- data/lib/sequel/adapters/shared/firebird.rb +12 -5
- data/lib/sequel/adapters/shared/informix.rb +11 -3
- data/lib/sequel/adapters/shared/mssql.rb +94 -47
- data/lib/sequel/adapters/shared/mysql.rb +107 -49
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +54 -27
- data/lib/sequel/adapters/shared/postgres.rb +65 -26
- data/lib/sequel/adapters/shared/progress.rb +4 -1
- data/lib/sequel/adapters/shared/sqlite.rb +36 -20
- data/lib/sequel/adapters/sqlite.rb +2 -3
- data/lib/sequel/adapters/swift/mysql.rb +3 -2
- data/lib/sequel/adapters/swift/sqlite.rb +2 -2
- data/lib/sequel/adapters/tinytds.rb +14 -8
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -4
- data/lib/sequel/database/misc.rb +6 -2
- data/lib/sequel/dataset/graph.rb +33 -7
- data/lib/sequel/dataset/prepared_statements.rb +19 -5
- data/lib/sequel/dataset/sql.rb +611 -201
- data/lib/sequel/model/associations.rb +12 -5
- data/lib/sequel/model/base.rb +20 -5
- data/lib/sequel/plugins/sharding.rb +9 -29
- data/lib/sequel/sql.rb +2 -1
- data/lib/sequel/timezones.rb +14 -4
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +10 -0
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/core/core_sql_spec.rb +3 -1
- data/spec/core/database_spec.rb +42 -0
- data/spec/core/dataset_spec.rb +10 -3
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/object_graph_spec.rb +38 -0
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +35 -39
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +30 -82
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +33 -103
- data/spec/extensions/instance_filters_spec.rb +10 -21
- data/spec/extensions/instance_hooks_spec.rb +6 -24
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +135 -277
- data/spec/extensions/migration_spec.rb +18 -15
- data/spec/extensions/named_timezones_spec.rb +1 -1
- data/spec/extensions/nested_attributes_spec.rb +97 -92
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +22 -37
- data/spec/extensions/prepared_statements_safe_spec.rb +9 -27
- data/spec/extensions/prepared_statements_spec.rb +11 -30
- data/spec/extensions/prepared_statements_with_pk_spec.rb +6 -13
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +3 -6
- data/spec/extensions/serialization_spec.rb +20 -32
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +14 -36
- data/spec/extensions/spec_helper.rb +10 -64
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +1 -2
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/integration/dataset_test.rb +16 -4
- data/spec/integration/prepared_statement_test.rb +4 -2
- data/spec/model/eager_loading_spec.rb +16 -0
- data/spec/model/model_spec.rb +15 -1
- data/spec/model/record_spec.rb +60 -0
- metadata +23 -40
|
@@ -8,27 +8,57 @@ module Sequel
|
|
|
8
8
|
# Database instance methods for SQLServer databases accessed via JDBC.
|
|
9
9
|
module DatabaseMethods
|
|
10
10
|
include Sequel::JDBC::MSSQL::DatabaseMethods
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
|
|
12
|
+
# Work around a bug in SQL Server JDBC Driver 3.0, where the metadata
|
|
13
|
+
# for the getColumns result set specifies an incorrect type for the
|
|
14
|
+
# IS_AUTOINCREMENT column. The column is a string, but the type is
|
|
15
|
+
# specified as a short. This causes getObject() to throw a
|
|
16
|
+
# com.microsoft.sqlserver.jdbc.SQLServerException: "The conversion
|
|
17
|
+
# from char to SMALLINT is unsupported." Using getString() rather
|
|
18
|
+
# than getObject() for this column avoids the problem.
|
|
19
|
+
# Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
|
|
20
|
+
module MetadataDatasetMethods
|
|
21
|
+
def process_result_set_convert(cols, result, rn)
|
|
22
|
+
while result.next
|
|
23
|
+
row = {}
|
|
24
|
+
cols.each do |n, i, p|
|
|
25
|
+
v = (n == :is_autoincrement ? result.getString(i) : result.getObject(i))
|
|
26
|
+
row[n] = if v
|
|
27
|
+
if p
|
|
28
|
+
p.call(v)
|
|
29
|
+
elsif p.nil?
|
|
30
|
+
cols[i-1][2] = p = convert_type_proc(v)
|
|
31
|
+
if p
|
|
32
|
+
p.call(v)
|
|
33
|
+
else
|
|
34
|
+
v
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
v
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
v
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
row.delete(rn) if rn
|
|
44
|
+
yield row
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def process_result_set_no_convert(cols, result, rn)
|
|
49
|
+
while result.next
|
|
50
|
+
row = {}
|
|
51
|
+
cols.each do |n, i|
|
|
52
|
+
row[n] = (n == :is_autoincrement ? result.getString(i) : result.getObject(i))
|
|
28
53
|
end
|
|
54
|
+
row.delete(rn) if rn
|
|
55
|
+
yield row
|
|
29
56
|
end
|
|
30
57
|
end
|
|
31
|
-
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def metadata_dataset
|
|
61
|
+
super.extend(MetadataDatasetMethods)
|
|
32
62
|
end
|
|
33
63
|
end
|
|
34
64
|
|
data/lib/sequel/adapters/mock.rb
CHANGED
|
@@ -92,8 +92,9 @@ module Sequel
|
|
|
92
92
|
# :numrows :: Call #numrows= with the value
|
|
93
93
|
# :extend :: A module the object is extended with.
|
|
94
94
|
# :sqls :: The array to store the SQL queries in.
|
|
95
|
-
def initialize(opts=
|
|
95
|
+
def initialize(opts={})
|
|
96
96
|
super
|
|
97
|
+
opts = @opts
|
|
97
98
|
self.autoid = opts[:autoid]
|
|
98
99
|
self.columns = opts[:columns]
|
|
99
100
|
self.fetch = opts[:fetch]
|
|
@@ -347,8 +347,10 @@ module Sequel
|
|
|
347
347
|
end
|
|
348
348
|
|
|
349
349
|
# Handle correct quoting of strings using ::MySQL.quote.
|
|
350
|
-
def
|
|
351
|
-
"'
|
|
350
|
+
def literal_string_append(sql, v)
|
|
351
|
+
sql << "'"
|
|
352
|
+
sql << ::Mysql.quote(v)
|
|
353
|
+
sql << "'"
|
|
352
354
|
end
|
|
353
355
|
|
|
354
356
|
# Yield each row of the given result set r with columns cols
|
|
@@ -172,8 +172,8 @@ module Sequel
|
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
# Handle correct quoting of strings using ::Mysql2::Client#escape.
|
|
175
|
-
def
|
|
176
|
-
db.synchronize{|c|
|
|
175
|
+
def literal_string_append(sql, v)
|
|
176
|
+
sql << "'" << db.synchronize{|c| c.escape(v)} << "'"
|
|
177
177
|
end
|
|
178
178
|
end
|
|
179
179
|
end
|
|
@@ -7,7 +7,7 @@ module Sequel
|
|
|
7
7
|
module MSSQL
|
|
8
8
|
module DatabaseMethods
|
|
9
9
|
include Sequel::MSSQL::DatabaseMethods
|
|
10
|
-
LAST_INSERT_ID_SQL='SELECT SCOPE_IDENTITY()'
|
|
10
|
+
LAST_INSERT_ID_SQL='SELECT SCOPE_IDENTITY()'.freeze
|
|
11
11
|
|
|
12
12
|
# Return the last inserted identity value.
|
|
13
13
|
def execute_insert(sql, opts={})
|
|
@@ -32,7 +32,7 @@ module Sequel
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
class Dataset < Sequel::Dataset
|
|
35
|
-
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
|
|
35
|
+
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
|
|
36
36
|
|
|
37
37
|
Database::DatasetClass = self
|
|
38
38
|
|
|
@@ -317,17 +317,17 @@ module Sequel
|
|
|
317
317
|
# Run execute_select on the database with the given SQL and the stored
|
|
318
318
|
# bind arguments.
|
|
319
319
|
def execute(sql, opts={}, &block)
|
|
320
|
-
super(
|
|
320
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
321
321
|
end
|
|
322
322
|
|
|
323
323
|
# Same as execute, explicit due to intricacies of alias and super.
|
|
324
324
|
def execute_dui(sql, opts={}, &block)
|
|
325
|
-
super(
|
|
325
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
326
326
|
end
|
|
327
327
|
|
|
328
328
|
# Same as execute, explicit due to intricacies of alias and super.
|
|
329
329
|
def execute_insert(sql, opts={}, &block)
|
|
330
|
-
super(
|
|
330
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
331
331
|
end
|
|
332
332
|
end
|
|
333
333
|
|
|
@@ -414,13 +414,13 @@ module Sequel
|
|
|
414
414
|
|
|
415
415
|
private
|
|
416
416
|
|
|
417
|
-
def
|
|
417
|
+
def literal_other_append(sql, v)
|
|
418
418
|
case v
|
|
419
419
|
when OraDate
|
|
420
|
-
|
|
420
|
+
literal_append(sql, db.to_application_timestamp(v))
|
|
421
421
|
when OCI8::CLOB
|
|
422
422
|
v.rewind
|
|
423
|
-
|
|
423
|
+
literal_append(sql, v.read)
|
|
424
424
|
else
|
|
425
425
|
super
|
|
426
426
|
end
|
|
@@ -218,14 +218,27 @@ module Sequel
|
|
|
218
218
|
# client encoding for the connection.
|
|
219
219
|
def connect(server)
|
|
220
220
|
opts = server_opts(server)
|
|
221
|
-
conn =
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
221
|
+
conn = if SEQUEL_POSTGRES_USES_PG
|
|
222
|
+
connection_params = {
|
|
223
|
+
:host => opts[:host],
|
|
224
|
+
:port => opts[:port] || 5432,
|
|
225
|
+
:tty => '',
|
|
226
|
+
:dbname => opts[:database],
|
|
227
|
+
:user => opts[:user],
|
|
228
|
+
:password => opts[:password],
|
|
229
|
+
:connect_timeout => opts[:connect_timeout] || 20
|
|
230
|
+
}.delete_if { |key, value| blank_object?(value) }
|
|
231
|
+
Adapter.connect(connection_params)
|
|
232
|
+
else
|
|
233
|
+
Adapter.connect(
|
|
234
|
+
(opts[:host] unless blank_object?(opts[:host])),
|
|
235
|
+
opts[:port] || 5432,
|
|
236
|
+
nil, '',
|
|
237
|
+
opts[:database],
|
|
238
|
+
opts[:user],
|
|
239
|
+
opts[:password]
|
|
240
|
+
)
|
|
241
|
+
end
|
|
229
242
|
if encoding = opts[:encoding] || opts[:charset]
|
|
230
243
|
if conn.respond_to?(:set_client_encoding)
|
|
231
244
|
conn.set_client_encoding(encoding)
|
|
@@ -639,13 +652,13 @@ module Sequel
|
|
|
639
652
|
end
|
|
640
653
|
|
|
641
654
|
# Use the driver's escape_bytea
|
|
642
|
-
def
|
|
643
|
-
db.synchronize{|c|
|
|
655
|
+
def literal_blob_append(sql, v)
|
|
656
|
+
sql << "'" << db.synchronize{|c| c.escape_bytea(v)} << "'"
|
|
644
657
|
end
|
|
645
658
|
|
|
646
659
|
# Use the driver's escape_string
|
|
647
|
-
def
|
|
648
|
-
db.synchronize{|c|
|
|
660
|
+
def literal_string_append(sql, v)
|
|
661
|
+
sql << "'" << db.synchronize{|c| c.escape_string(v)} << "'"
|
|
649
662
|
end
|
|
650
663
|
|
|
651
664
|
# For each row in the result set, yield a hash with column name symbol
|
|
@@ -28,7 +28,12 @@ module Sequel
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
module DatasetMethods
|
|
31
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
|
|
31
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where group order having compounds')
|
|
32
|
+
DATE_FORMAT = '#%Y-%m-%d#'.freeze
|
|
33
|
+
TIMESTAMP_FORMAT = '#%Y-%m-%d %H:%M:%S#'.freeze
|
|
34
|
+
TOP = " TOP ".freeze
|
|
35
|
+
BRACKET_CLOSE = Dataset::BRACKET_CLOSE
|
|
36
|
+
BRACKET_OPEN = Dataset::BRACKET_OPEN
|
|
32
37
|
|
|
33
38
|
# Access doesn't support INTERSECT or EXCEPT
|
|
34
39
|
def supports_intersect_except?
|
|
@@ -39,23 +44,26 @@ module Sequel
|
|
|
39
44
|
|
|
40
45
|
# Access uses # to quote dates
|
|
41
46
|
def literal_date(d)
|
|
42
|
-
d.strftime(
|
|
47
|
+
d.strftime(DATE_FORMAT)
|
|
43
48
|
end
|
|
44
49
|
|
|
45
50
|
# Access uses # to quote datetimes
|
|
46
51
|
def literal_datetime(t)
|
|
47
|
-
t.strftime(
|
|
52
|
+
t.strftime(TIMESTAMP_FORMAT)
|
|
48
53
|
end
|
|
49
54
|
alias literal_time literal_datetime
|
|
50
55
|
|
|
51
56
|
# Access uses TOP for limits
|
|
52
57
|
def select_limit_sql(sql)
|
|
53
|
-
|
|
58
|
+
if l = @opts[:limit]
|
|
59
|
+
sql << TOP
|
|
60
|
+
literal_append(sql, l)
|
|
61
|
+
end
|
|
54
62
|
end
|
|
55
63
|
|
|
56
64
|
# Access uses [] for quoting identifiers
|
|
57
|
-
def
|
|
58
|
-
|
|
65
|
+
def quoted_identifier_append(sql, v)
|
|
66
|
+
sql << BRACKET_OPEN << v.to_s << BRACKET_CLOSE
|
|
59
67
|
end
|
|
60
68
|
|
|
61
69
|
# Access requires the limit clause come before other clauses
|
|
@@ -175,35 +175,52 @@ module Sequel
|
|
|
175
175
|
module DatasetMethods
|
|
176
176
|
include EmulateOffsetWithRowNumber
|
|
177
177
|
|
|
178
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
179
|
+
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
178
180
|
BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR, :'B~'=>:BITNOT}
|
|
179
181
|
BOOL_TRUE = '1'.freeze
|
|
180
182
|
BOOL_FALSE = '0'.freeze
|
|
181
|
-
|
|
183
|
+
CAST_STRING_OPEN = "RTRIM(CHAR(".freeze
|
|
184
|
+
CAST_STRING_CLOSE = "))".freeze
|
|
185
|
+
FETCH_FIRST_ROW_ONLY = " FETCH FIRST ROW ONLY".freeze
|
|
186
|
+
FETCH_FIRST = " FETCH FIRST ".freeze
|
|
187
|
+
ROWS_ONLY = " ROWS ONLY".freeze
|
|
188
|
+
EMPTY_FROM_TABLE = ' FROM "SYSIBM"."SYSDUMMY1"'.freeze
|
|
189
|
+
|
|
182
190
|
# DB2 casts strings using RTRIM and CHAR instead of VARCHAR.
|
|
183
|
-
def
|
|
184
|
-
type == String
|
|
191
|
+
def cast_sql_append(sql, expr, type)
|
|
192
|
+
if(type == String)
|
|
193
|
+
sql << CAST_STRING_OPEN
|
|
194
|
+
literal_append(sql, expr)
|
|
195
|
+
sql << CAST_STRING_CLOSE
|
|
196
|
+
else
|
|
197
|
+
super
|
|
198
|
+
end
|
|
185
199
|
end
|
|
186
200
|
|
|
187
201
|
# Handle DB2 specific LIKE and bitwise operator support, and
|
|
188
202
|
# emulate the extract method, which DB2 doesn't natively support.
|
|
189
|
-
def
|
|
203
|
+
def complex_expression_sql_append(sql, op, args)
|
|
190
204
|
case op
|
|
191
205
|
when :ILIKE
|
|
192
|
-
super(:LIKE, [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
|
|
206
|
+
super(sql, :LIKE, [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
|
|
193
207
|
when :"NOT ILIKE"
|
|
194
|
-
super(:"NOT LIKE", [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
|
|
208
|
+
super(sql, :"NOT LIKE", [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
|
|
195
209
|
when :&, :|, :^
|
|
196
210
|
# works with db2 v9.5 and after
|
|
197
211
|
op = BITWISE_METHOD_MAP[op]
|
|
198
|
-
complex_expression_arg_pairs(args){|a, b| literal(SQL::Function.new(op, a, b))}
|
|
212
|
+
sql << complex_expression_arg_pairs(args){|a, b| literal(SQL::Function.new(op, a, b))}
|
|
199
213
|
when :<<
|
|
200
|
-
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
214
|
+
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
201
215
|
when :>>
|
|
202
|
-
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
216
|
+
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
203
217
|
when :'B~'
|
|
204
|
-
|
|
218
|
+
literal_append(sql, SQL::Function.new(:BITNOT, *args))
|
|
205
219
|
when :extract
|
|
206
|
-
|
|
220
|
+
sql << args.at(0).to_s
|
|
221
|
+
sql << PAREN_OPEN
|
|
222
|
+
literal_append(sql, args.at(1))
|
|
223
|
+
sql << PAREN_CLOSE
|
|
207
224
|
else
|
|
208
225
|
super
|
|
209
226
|
end
|
|
@@ -259,7 +276,7 @@ module Sequel
|
|
|
259
276
|
|
|
260
277
|
# Add a fallback table for empty from situation
|
|
261
278
|
def select_from_sql(sql)
|
|
262
|
-
@opts[:from] ? super : (sql <<
|
|
279
|
+
@opts[:from] ? super : (sql << EMPTY_FROM_TABLE)
|
|
263
280
|
end
|
|
264
281
|
|
|
265
282
|
# Modify the sql to limit the number of rows returned
|
|
@@ -274,7 +291,13 @@ module Sequel
|
|
|
274
291
|
# Support for this feature is not used in this adapter however.
|
|
275
292
|
def select_limit_sql(sql)
|
|
276
293
|
if l = @opts[:limit]
|
|
277
|
-
|
|
294
|
+
if l == 1
|
|
295
|
+
sql << FETCH_FIRST_ROW_ONLY
|
|
296
|
+
elsif l > 1
|
|
297
|
+
sql << FETCH_FIRST
|
|
298
|
+
literal_append(sql, l)
|
|
299
|
+
sql << ROWS_ONLY
|
|
300
|
+
end
|
|
278
301
|
end
|
|
279
302
|
end
|
|
280
303
|
|
|
@@ -150,9 +150,10 @@ module Sequel
|
|
|
150
150
|
BOOL_TRUE = '1'.freeze
|
|
151
151
|
BOOL_FALSE = '0'.freeze
|
|
152
152
|
NULL = LiteralString.new('NULL').freeze
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns from join where group having compounds order')
|
|
154
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
|
|
155
|
+
FIRST = " FIRST ".freeze
|
|
156
|
+
SKIP = " SKIP ".freeze
|
|
156
157
|
|
|
157
158
|
# Insert given values into the database.
|
|
158
159
|
def insert(*values)
|
|
@@ -206,8 +207,14 @@ module Sequel
|
|
|
206
207
|
end
|
|
207
208
|
|
|
208
209
|
def select_limit_sql(sql)
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
if l = @opts[:limit]
|
|
211
|
+
sql << FIRST
|
|
212
|
+
literal_append(sql, l)
|
|
213
|
+
end
|
|
214
|
+
if o = @opts[:offset]
|
|
215
|
+
sql << SKIP
|
|
216
|
+
literal_append(sql, o)
|
|
217
|
+
end
|
|
211
218
|
end
|
|
212
219
|
end
|
|
213
220
|
end
|
|
@@ -23,7 +23,9 @@ module Sequel
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
module DatasetMethods
|
|
26
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where having group compounds order')
|
|
26
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where having group compounds order')
|
|
27
|
+
FIRST = " FIRST ".freeze
|
|
28
|
+
SKIP = " SKIP ".freeze
|
|
27
29
|
|
|
28
30
|
private
|
|
29
31
|
|
|
@@ -37,8 +39,14 @@ module Sequel
|
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
def select_limit_sql(sql)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
if o = @opts[:offset]
|
|
43
|
+
sql << SKIP
|
|
44
|
+
literal_append(sql, o)
|
|
45
|
+
end
|
|
46
|
+
if l = @opts[:limit]
|
|
47
|
+
sql << FIRST
|
|
48
|
+
literal_append(sql, l)
|
|
49
|
+
end
|
|
42
50
|
end
|
|
43
51
|
end
|
|
44
52
|
end
|
|
@@ -249,15 +249,42 @@ module Sequel
|
|
|
249
249
|
BOOL_TRUE = '1'.freeze
|
|
250
250
|
BOOL_FALSE = '0'.freeze
|
|
251
251
|
COMMA_SEPARATOR = ', '.freeze
|
|
252
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with from output from2 where')
|
|
253
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with into columns output values')
|
|
254
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct limit columns into from lock join where group having order compounds')
|
|
255
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with table set output from where')
|
|
252
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with delete from output from2 where')
|
|
253
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with insert into columns output values')
|
|
254
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns into from lock join where group having order compounds')
|
|
255
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with update table set output from where')
|
|
256
256
|
NOLOCK = ' WITH (NOLOCK)'.freeze
|
|
257
257
|
UPDLOCK = ' WITH (UPDLOCK)'.freeze
|
|
258
258
|
WILDCARD = LiteralString.new('*').freeze
|
|
259
259
|
CONSTANT_MAP = {:CURRENT_DATE=>'CAST(CURRENT_TIMESTAMP AS DATE)'.freeze, :CURRENT_TIME=>'CAST(CURRENT_TIMESTAMP AS TIME)'.freeze}
|
|
260
260
|
EXTRACT_MAP = {:year=>"yy", :month=>"m", :day=>"d", :hour=>"hh", :minute=>"n", :second=>"s"}
|
|
261
|
+
BRACKET_CLOSE = Dataset::BRACKET_CLOSE
|
|
262
|
+
BRACKET_OPEN = Dataset::BRACKET_OPEN
|
|
263
|
+
COMMA = Dataset::COMMA
|
|
264
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
265
|
+
PAREN_SPACE_OPEN = Dataset::PAREN_SPACE_OPEN
|
|
266
|
+
SPACE = Dataset::SPACE
|
|
267
|
+
FROM = Dataset::FROM
|
|
268
|
+
APOS = Dataset::APOS
|
|
269
|
+
APOS_RE = Dataset::APOS_RE
|
|
270
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
271
|
+
INTO = Dataset::INTO
|
|
272
|
+
DATEPART_SECOND_OPEN = "CAST((datepart(".freeze
|
|
273
|
+
DATEPART_SECOND_MIDDLE = ') + datepart(ns, '.freeze
|
|
274
|
+
DATEPART_SECOND_CLOSE = ")/1000000000.0) AS double precision)".freeze
|
|
275
|
+
DATEPART_OPEN = "datepart(".freeze
|
|
276
|
+
UNION_ALL = ' UNION ALL '.freeze
|
|
277
|
+
SELECT_SPACE = 'SELECT '.freeze
|
|
278
|
+
TIMESTAMP_USEC_FORMAT = ".%03d".freeze
|
|
279
|
+
OUTPUT_INSERTED = " OUTPUT INSERTED.*".freeze
|
|
280
|
+
HEX_START = '0x'.freeze
|
|
281
|
+
UNICODE_STRING_START = "N'".freeze
|
|
282
|
+
TOP_PAREN = " TOP (".freeze
|
|
283
|
+
TOP = " TOP ".freeze
|
|
284
|
+
OUTPUT = " OUTPUT ".freeze
|
|
285
|
+
HSTAR = "H*".freeze
|
|
286
|
+
CASE_SENSITIVE_COLLATION = 'Latin1_General_CS_AS'.freeze
|
|
287
|
+
CASE_INSENSITIVE_COLLATION = 'Latin1_General_CI_AS'.freeze
|
|
261
288
|
|
|
262
289
|
# Allow overriding of the mssql_unicode_strings option at the dataset level.
|
|
263
290
|
attr_accessor :mssql_unicode_strings
|
|
@@ -269,33 +296,41 @@ module Sequel
|
|
|
269
296
|
end
|
|
270
297
|
|
|
271
298
|
# MSSQL uses + for string concatenation, and LIKE is case insensitive by default.
|
|
272
|
-
def
|
|
299
|
+
def complex_expression_sql_append(sql, op, args)
|
|
273
300
|
case op
|
|
274
301
|
when :'||'
|
|
275
|
-
super(:+, args)
|
|
302
|
+
super(sql, :+, args)
|
|
276
303
|
when :LIKE, :"NOT LIKE"
|
|
277
|
-
super(op, args.map{|a| LiteralString.new("(#{literal(a)} COLLATE
|
|
304
|
+
super(sql, op, args.map{|a| LiteralString.new("(#{literal(a)} COLLATE #{CASE_SENSITIVE_COLLATION})")})
|
|
278
305
|
when :ILIKE, :"NOT ILIKE"
|
|
279
|
-
super((op == :ILIKE ? :LIKE : :"NOT LIKE"), args)
|
|
306
|
+
super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), args.map{|a| LiteralString.new("(#{literal(a)} COLLATE #{CASE_INSENSITIVE_COLLATION})")})
|
|
280
307
|
when :<<
|
|
281
|
-
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
308
|
+
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
282
309
|
when :>>
|
|
283
|
-
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
310
|
+
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
284
311
|
when :extract
|
|
285
312
|
part = args.at(0)
|
|
286
313
|
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
314
|
+
if part == :second
|
|
315
|
+
expr = literal(args.at(1))
|
|
316
|
+
sql << DATEPART_SECOND_OPEN << format.to_s << COMMA << expr << DATEPART_SECOND_MIDDLE << expr << DATEPART_SECOND_CLOSE
|
|
317
|
+
else
|
|
318
|
+
sql << DATEPART_OPEN << format.to_s << COMMA
|
|
319
|
+
literal_append(sql, args.at(1))
|
|
320
|
+
sql << PAREN_CLOSE
|
|
321
|
+
end
|
|
291
322
|
else
|
|
292
|
-
super
|
|
323
|
+
super
|
|
293
324
|
end
|
|
294
325
|
end
|
|
295
326
|
|
|
296
327
|
# MSSQL doesn't support the SQL standard CURRENT_DATE or CURRENT_TIME
|
|
297
|
-
def
|
|
298
|
-
CONSTANT_MAP[constant]
|
|
328
|
+
def constant_sql_append(sql, constant)
|
|
329
|
+
if c = CONSTANT_MAP[constant]
|
|
330
|
+
sql << c
|
|
331
|
+
else
|
|
332
|
+
super
|
|
333
|
+
end
|
|
299
334
|
end
|
|
300
335
|
|
|
301
336
|
# Disable the use of INSERT OUTPUT
|
|
@@ -326,7 +361,16 @@ module Sequel
|
|
|
326
361
|
|
|
327
362
|
# MSSQL uses a UNION ALL statement to insert multiple values at once.
|
|
328
363
|
def multi_insert_sql(columns, values)
|
|
329
|
-
|
|
364
|
+
c = false
|
|
365
|
+
sql = LiteralString.new('')
|
|
366
|
+
u = UNION_ALL
|
|
367
|
+
values.each do |v|
|
|
368
|
+
sql << u if c
|
|
369
|
+
sql << SELECT_SPACE
|
|
370
|
+
expression_list_append(sql, v)
|
|
371
|
+
c ||= true
|
|
372
|
+
end
|
|
373
|
+
[insert_sql(columns, sql)]
|
|
330
374
|
end
|
|
331
375
|
|
|
332
376
|
# Allows you to do a dirty read of uncommitted data using WITH (NOLOCK).
|
|
@@ -365,8 +409,8 @@ module Sequel
|
|
|
365
409
|
end
|
|
366
410
|
|
|
367
411
|
# MSSQL uses [] to quote identifiers
|
|
368
|
-
def
|
|
369
|
-
|
|
412
|
+
def quoted_identifier_append(sql, name)
|
|
413
|
+
sql << BRACKET_OPEN << name.to_s << BRACKET_CLOSE
|
|
370
414
|
end
|
|
371
415
|
|
|
372
416
|
# The version of the database server.
|
|
@@ -439,7 +483,8 @@ module Sequel
|
|
|
439
483
|
|
|
440
484
|
# Only include the primary table in the main delete clause
|
|
441
485
|
def delete_from_sql(sql)
|
|
442
|
-
sql <<
|
|
486
|
+
sql << FROM
|
|
487
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
443
488
|
end
|
|
444
489
|
|
|
445
490
|
# MSSQL supports FROM clauses in DELETE and UPDATE statements.
|
|
@@ -451,19 +496,11 @@ module Sequel
|
|
|
451
496
|
end
|
|
452
497
|
alias update_from_sql delete_from2_sql
|
|
453
498
|
|
|
454
|
-
# Handle the with clause for delete, insert, and update statements
|
|
455
|
-
# to be the same as the insert statement.
|
|
456
|
-
def delete_with_sql(sql)
|
|
457
|
-
select_with_sql(sql)
|
|
458
|
-
end
|
|
459
|
-
alias insert_with_sql delete_with_sql
|
|
460
|
-
alias update_with_sql delete_with_sql
|
|
461
|
-
|
|
462
499
|
# MSSQL raises an error if you try to provide more than 3 decimal places
|
|
463
500
|
# for a fractional timestamp. This probably doesn't work for smalldatetime
|
|
464
501
|
# fields.
|
|
465
502
|
def format_timestamp_usec(usec)
|
|
466
|
-
sprintf(
|
|
503
|
+
sprintf(TIMESTAMP_USEC_FORMAT, usec/1000)
|
|
467
504
|
end
|
|
468
505
|
|
|
469
506
|
# MSSQL supports the OUTPUT clause for INSERT statements.
|
|
@@ -476,23 +513,22 @@ module Sequel
|
|
|
476
513
|
# for use with the prepared statement code.
|
|
477
514
|
def insert_output_sql(sql)
|
|
478
515
|
if @opts.has_key?(:returning)
|
|
479
|
-
sql <<
|
|
516
|
+
sql << OUTPUT_INSERTED
|
|
480
517
|
else
|
|
481
518
|
output_sql(sql)
|
|
482
519
|
end
|
|
483
520
|
end
|
|
484
521
|
|
|
485
522
|
# MSSQL uses a literal hexidecimal number for blob strings
|
|
486
|
-
def
|
|
487
|
-
|
|
488
|
-
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
489
|
-
blob
|
|
523
|
+
def literal_blob_append(sql, v)
|
|
524
|
+
sql << HEX_START << v.unpack(HSTAR).first
|
|
490
525
|
end
|
|
491
526
|
|
|
492
527
|
# Optionally use unicode string syntax for all strings. Don't double
|
|
493
528
|
# backslashes.
|
|
494
|
-
def
|
|
495
|
-
|
|
529
|
+
def literal_string_append(sql, v)
|
|
530
|
+
sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
|
|
531
|
+
sql << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
496
532
|
end
|
|
497
533
|
|
|
498
534
|
# Use 0 for false on MSSQL
|
|
@@ -511,16 +547,24 @@ module Sequel
|
|
|
511
547
|
end
|
|
512
548
|
|
|
513
549
|
def select_into_sql(sql)
|
|
514
|
-
|
|
550
|
+
if i = @opts[:into]
|
|
551
|
+
sql << INTO
|
|
552
|
+
table_ref_append(sql, i)
|
|
553
|
+
end
|
|
515
554
|
end
|
|
516
555
|
|
|
517
556
|
# MSSQL uses TOP N for limit. For MSSQL 2005+ TOP (N) is used
|
|
518
557
|
# to allow the limit to be a bound variable.
|
|
519
558
|
def select_limit_sql(sql)
|
|
520
559
|
if l = @opts[:limit]
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
560
|
+
if is_2005_or_later?
|
|
561
|
+
sql << TOP_PAREN
|
|
562
|
+
literal_append(sql, l)
|
|
563
|
+
sql << PAREN_CLOSE
|
|
564
|
+
else
|
|
565
|
+
sql << TOP
|
|
566
|
+
literal_append(sql, l)
|
|
567
|
+
end
|
|
524
568
|
end
|
|
525
569
|
end
|
|
526
570
|
|
|
@@ -540,13 +584,15 @@ module Sequel
|
|
|
540
584
|
def output_sql(sql)
|
|
541
585
|
return unless supports_output_clause?
|
|
542
586
|
return unless output = @opts[:output]
|
|
543
|
-
sql <<
|
|
587
|
+
sql << OUTPUT
|
|
588
|
+
column_list_append(sql, output[:select_list])
|
|
544
589
|
if into = output[:into]
|
|
545
|
-
sql <<
|
|
590
|
+
sql << INTO
|
|
591
|
+
table_ref_append(sql, into)
|
|
546
592
|
if column_list = output[:column_list]
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
sql <<
|
|
593
|
+
sql << PAREN_SPACE_OPEN
|
|
594
|
+
source_list_append(sql, column_list)
|
|
595
|
+
sql << PAREN_CLOSE
|
|
550
596
|
end
|
|
551
597
|
end
|
|
552
598
|
end
|
|
@@ -561,7 +607,8 @@ module Sequel
|
|
|
561
607
|
|
|
562
608
|
# Only include the primary table in the main update clause
|
|
563
609
|
def update_table_sql(sql)
|
|
564
|
-
sql <<
|
|
610
|
+
sql << SPACE
|
|
611
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
565
612
|
end
|
|
566
613
|
end
|
|
567
614
|
end
|