sequel 3.34.1 → 3.35.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 +52 -0
- data/README.rdoc +3 -1
- data/Rakefile +2 -10
- data/doc/active_record.rdoc +1 -0
- data/doc/migration.rdoc +18 -7
- data/doc/model_hooks.rdoc +6 -0
- data/doc/opening_databases.rdoc +3 -0
- data/doc/prepared_statements.rdoc +0 -1
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/schema_modification.rdoc +16 -1
- data/doc/thread_safety.rdoc +17 -0
- data/lib/sequel/adapters/do.rb +2 -2
- data/lib/sequel/adapters/do/postgres.rb +1 -52
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +23 -19
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +29 -2
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
- data/lib/sequel/adapters/mysql2.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +3 -3
- data/lib/sequel/adapters/odbc/mssql.rb +14 -1
- data/lib/sequel/adapters/oracle.rb +6 -18
- data/lib/sequel/adapters/postgres.rb +36 -53
- data/lib/sequel/adapters/shared/db2.rb +16 -2
- data/lib/sequel/adapters/shared/mssql.rb +40 -9
- data/lib/sequel/adapters/shared/mysql.rb +16 -4
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +2 -0
- data/lib/sequel/adapters/shared/postgres.rb +135 -211
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/swift.rb +1 -1
- data/lib/sequel/adapters/swift/postgres.rb +1 -71
- data/lib/sequel/adapters/tinytds.rb +3 -3
- data/lib/sequel/core.rb +27 -4
- data/lib/sequel/database/connecting.rb +7 -8
- data/lib/sequel/database/logging.rb +6 -1
- data/lib/sequel/database/misc.rb +20 -4
- data/lib/sequel/database/query.rb +38 -18
- data/lib/sequel/database/schema_generator.rb +5 -2
- data/lib/sequel/database/schema_methods.rb +34 -8
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/sql.rb +18 -24
- data/lib/sequel/extensions/core_extensions.rb +0 -23
- data/lib/sequel/extensions/migration.rb +22 -8
- data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/model.rb +2 -2
- data/lib/sequel/model/associations.rb +95 -70
- data/lib/sequel/model/base.rb +16 -18
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +16 -1
- data/lib/sequel/plugins/many_through_many.rb +22 -32
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
- data/lib/sequel/plugins/prepared_statements.rb +22 -8
- data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +12 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/postgres_spec.rb +30 -79
- data/spec/core/database_spec.rb +46 -2
- data/spec/core/dataset_spec.rb +28 -22
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +51 -0
- data/spec/extensions/arbitrary_servers_spec.rb +0 -4
- data/spec/extensions/association_autoreloading_spec.rb +17 -0
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/core_extensions_spec.rb +1 -24
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/json_serializer_spec.rb +13 -0
- data/spec/extensions/migration_spec.rb +28 -15
- data/spec/extensions/named_timezones_spec.rb +6 -8
- data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
- data/spec/extensions/schema_dumper_spec.rb +3 -1
- data/spec/extensions/xml_serializer_spec.rb +13 -0
- data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
- data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
- data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
- data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
- data/spec/integration/associations_test.rb +5 -7
- data/spec/integration/dataset_test.rb +25 -7
- data/spec/integration/plugin_test.rb +1 -1
- data/spec/integration/schema_test.rb +16 -1
- data/spec/model/associations_spec.rb +2 -2
- metadata +14 -9
- data/lib/sequel/adapters/odbc/db2.rb +0 -17
|
@@ -28,9 +28,22 @@ module Sequel
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
|
-
|
|
32
31
|
class Dataset < ODBC::Dataset
|
|
33
32
|
include Sequel::MSSQL::DatasetMethods
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
|
37
|
+
# some translation.
|
|
38
|
+
def default_timestamp_format
|
|
39
|
+
TIMESTAMP_FORMAT
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
|
43
|
+
# some translation.
|
|
44
|
+
def literal_date(v)
|
|
45
|
+
v.strftime(ODBC_DATE_FORMAT)
|
|
46
|
+
end
|
|
34
47
|
end
|
|
35
48
|
end
|
|
36
49
|
end
|
|
@@ -126,7 +126,7 @@ module Sequel
|
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def execute_prepared_statement(conn, type, name, opts)
|
|
129
|
-
ps =
|
|
129
|
+
ps = prepared_statement(name)
|
|
130
130
|
sql = ps.prepared_sql
|
|
131
131
|
if cursora = conn.prepared_statements[name]
|
|
132
132
|
cursor, cursor_sql = cursora
|
|
@@ -243,7 +243,7 @@ module Sequel
|
|
|
243
243
|
|
|
244
244
|
# Default values
|
|
245
245
|
defaults = begin
|
|
246
|
-
metadata_dataset.from(:
|
|
246
|
+
metadata_dataset.from(:user_tab_cols).
|
|
247
247
|
where(:table_name=>im.call(table)).
|
|
248
248
|
to_hash(:column_name, :data_default)
|
|
249
249
|
rescue DatabaseError
|
|
@@ -370,19 +370,6 @@ module Sequel
|
|
|
370
370
|
ps.call(bind_vars, &block)
|
|
371
371
|
end
|
|
372
372
|
|
|
373
|
-
# Prepare the given type of query with the given name and store
|
|
374
|
-
# it in the database. Note that a new native prepared statement is
|
|
375
|
-
# created on each call to this prepared statement.
|
|
376
|
-
def prepare(type, name=nil, *values)
|
|
377
|
-
ps = to_prepared_statement(type, values)
|
|
378
|
-
ps.extend(PreparedStatementMethods)
|
|
379
|
-
if name
|
|
380
|
-
ps.prepared_statement_name = name
|
|
381
|
-
db.prepared_statements[name] = ps
|
|
382
|
-
end
|
|
383
|
-
ps
|
|
384
|
-
end
|
|
385
|
-
|
|
386
373
|
def fetch_rows(sql)
|
|
387
374
|
execute(sql) do |cursor|
|
|
388
375
|
offset = @opts[:offset]
|
|
@@ -403,14 +390,15 @@ module Sequel
|
|
|
403
390
|
self
|
|
404
391
|
end
|
|
405
392
|
|
|
406
|
-
#
|
|
407
|
-
# database
|
|
393
|
+
# Prepare the given type of query with the given name and store
|
|
394
|
+
# it in the database. Note that a new native prepared statement is
|
|
395
|
+
# created on each call to this prepared statement.
|
|
408
396
|
def prepare(type, name=nil, *values)
|
|
409
397
|
ps = to_prepared_statement(type, values)
|
|
410
398
|
ps.extend(PreparedStatementMethods)
|
|
411
399
|
if name
|
|
412
400
|
ps.prepared_statement_name = name
|
|
413
|
-
db.
|
|
401
|
+
db.set_prepared_statement(name, ps)
|
|
414
402
|
end
|
|
415
403
|
ps
|
|
416
404
|
end
|
|
@@ -12,7 +12,7 @@ rescue LoadError => e
|
|
|
12
12
|
class PGconn
|
|
13
13
|
unless method_defined?(:escape_string)
|
|
14
14
|
if self.respond_to?(:escape)
|
|
15
|
-
# If there is no escape_string
|
|
15
|
+
# If there is no escape_string instance method, but there is an
|
|
16
16
|
# escape class method, use that instead.
|
|
17
17
|
def escape_string(str)
|
|
18
18
|
Sequel::Postgres.force_standard_strings ? str.gsub("'", "''") : self.class.escape(str)
|
|
@@ -91,24 +91,29 @@ module Sequel
|
|
|
91
91
|
NAN = 0.0/0.0
|
|
92
92
|
PLUS_INFINITY = 1.0/0.0
|
|
93
93
|
MINUS_INFINITY = -1.0/0.0
|
|
94
|
+
NAN_STR = 'NaN'.freeze
|
|
95
|
+
PLUS_INFINITY_STR = 'Infinity'.freeze
|
|
96
|
+
MINUS_INFINITY_STR = '-Infinity'.freeze
|
|
97
|
+
TRUE_STR = 't'.freeze
|
|
98
|
+
DASH_STR = '-'.freeze
|
|
94
99
|
|
|
95
100
|
TYPE_TRANSLATOR = tt = Class.new do
|
|
96
|
-
def boolean(s) s ==
|
|
101
|
+
def boolean(s) s == TRUE_STR end
|
|
97
102
|
def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
|
|
98
103
|
def integer(s) s.to_i end
|
|
99
104
|
def float(s)
|
|
100
105
|
case s
|
|
101
|
-
when
|
|
106
|
+
when NAN_STR
|
|
102
107
|
NAN
|
|
103
|
-
when
|
|
108
|
+
when PLUS_INFINITY_STR
|
|
104
109
|
PLUS_INFINITY
|
|
105
|
-
when
|
|
110
|
+
when MINUS_INFINITY_STR
|
|
106
111
|
MINUS_INFINITY
|
|
107
112
|
else
|
|
108
113
|
s.to_f
|
|
109
114
|
end
|
|
110
115
|
end
|
|
111
|
-
def date(s) ::Date.new(*s.split(
|
|
116
|
+
def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
|
|
112
117
|
end.new
|
|
113
118
|
|
|
114
119
|
# Hash with type name strings/symbols and callable values for converting PostgreSQL types.
|
|
@@ -149,8 +154,6 @@ module Sequel
|
|
|
149
154
|
class Adapter < ::PGconn
|
|
150
155
|
DISCONNECT_ERROR_RE = /\Acould not receive data from server: Software caused connection abort/
|
|
151
156
|
|
|
152
|
-
include Sequel::Postgres::AdapterMethods
|
|
153
|
-
|
|
154
157
|
self.translate_results = false if respond_to?(:translate_results=)
|
|
155
158
|
|
|
156
159
|
# Hash of prepared statements for this connection. Keys are
|
|
@@ -158,18 +161,6 @@ module Sequel
|
|
|
158
161
|
# are SQL strings.
|
|
159
162
|
attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
|
|
160
163
|
|
|
161
|
-
# Apply connection settings for this connection. Current sets
|
|
162
|
-
# the date style to ISO in order make Date object creation in ruby faster,
|
|
163
|
-
# if Postgres.use_iso_date_format is true.
|
|
164
|
-
def apply_connection_settings
|
|
165
|
-
super
|
|
166
|
-
if Postgres.use_iso_date_format
|
|
167
|
-
sql = "SET DateStyle = 'ISO'"
|
|
168
|
-
execute(sql)
|
|
169
|
-
end
|
|
170
|
-
@prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
|
|
171
|
-
end
|
|
172
|
-
|
|
173
164
|
# Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
|
|
174
165
|
# the connection status cannot be determined or it is not OK.
|
|
175
166
|
def check_disconnect_errors
|
|
@@ -210,11 +201,6 @@ module Sequel
|
|
|
210
201
|
def execute_query(sql, args)
|
|
211
202
|
@db.log_yield(sql, args){args ? async_exec(sql, args) : async_exec(sql)}
|
|
212
203
|
end
|
|
213
|
-
|
|
214
|
-
# Return the requested values for the given row.
|
|
215
|
-
def single_value(r)
|
|
216
|
-
r.getvalue(0, 0) unless r.nil? || (r.ntuples == 0)
|
|
217
|
-
end
|
|
218
204
|
end
|
|
219
205
|
|
|
220
206
|
# Database class for PostgreSQL databases used with Sequel and the
|
|
@@ -223,7 +209,7 @@ module Sequel
|
|
|
223
209
|
include Sequel::Postgres::DatabaseMethods
|
|
224
210
|
|
|
225
211
|
INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
|
|
226
|
-
INFINITE_DATETIME_VALUES = ([
|
|
212
|
+
INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
|
|
227
213
|
|
|
228
214
|
set_adapter_scheme :postgres
|
|
229
215
|
|
|
@@ -299,8 +285,9 @@ module Sequel
|
|
|
299
285
|
conn.async_exec("set client_encoding to '#{encoding}'")
|
|
300
286
|
end
|
|
301
287
|
end
|
|
302
|
-
conn.db
|
|
303
|
-
conn.
|
|
288
|
+
conn.instance_variable_set(:@db, self)
|
|
289
|
+
conn.instance_variable_set(:@prepared_statements, {}) if SEQUEL_POSTGRES_USES_PG
|
|
290
|
+
connection_configuration_sqls.each{|sql| conn.execute(sql)}
|
|
304
291
|
@conversion_procs ||= get_conversion_procs(conn)
|
|
305
292
|
conn
|
|
306
293
|
end
|
|
@@ -312,19 +299,7 @@ module Sequel
|
|
|
312
299
|
synchronize(opts[:server]){|conn| conn.execute(sql, opts[:arguments], &block)}
|
|
313
300
|
end
|
|
314
301
|
end
|
|
315
|
-
|
|
316
|
-
# Insert the values into the table and return the primary key (if
|
|
317
|
-
# automatically generated).
|
|
318
|
-
def execute_insert(sql, opts={})
|
|
319
|
-
return execute(sql, opts) if Symbol === sql
|
|
320
|
-
check_database_errors do
|
|
321
|
-
synchronize(opts[:server]) do |conn|
|
|
322
|
-
conn.execute(sql, opts[:arguments])
|
|
323
|
-
insert_result(conn, opts[:table], opts[:values])
|
|
324
|
-
end
|
|
325
|
-
end
|
|
326
|
-
end
|
|
327
|
-
|
|
302
|
+
|
|
328
303
|
if SEQUEL_POSTGRES_USES_PG
|
|
329
304
|
# +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
|
|
330
305
|
# results directly to the caller. This method is only supported if pg is the
|
|
@@ -470,6 +445,13 @@ module Sequel
|
|
|
470
445
|
end
|
|
471
446
|
end
|
|
472
447
|
|
|
448
|
+
# Set the DateStyle to ISO if configured, for faster date parsing.
|
|
449
|
+
def connection_configuration_sqls
|
|
450
|
+
sqls = super
|
|
451
|
+
sqls << "SET DateStyle = 'ISO'" if Postgres.use_iso_date_format
|
|
452
|
+
sqls
|
|
453
|
+
end
|
|
454
|
+
|
|
473
455
|
# Disconnect given connection
|
|
474
456
|
def disconnect_connection(conn)
|
|
475
457
|
begin
|
|
@@ -487,7 +469,7 @@ module Sequel
|
|
|
487
469
|
# of rows changed. If the :insert option is passed, return the value
|
|
488
470
|
# of the primary key for the last inserted row.
|
|
489
471
|
def execute_prepared_statement(name, opts={})
|
|
490
|
-
ps =
|
|
472
|
+
ps = prepared_statement(name)
|
|
491
473
|
sql = ps.prepared_sql
|
|
492
474
|
ps_name = name.to_s
|
|
493
475
|
synchronize(opts[:server]) do |conn|
|
|
@@ -511,14 +493,10 @@ module Sequel
|
|
|
511
493
|
end
|
|
512
494
|
|
|
513
495
|
q = conn.check_disconnect_errors{log_yield(log_sql, args){conn.exec_prepared(ps_name, args)}}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
block_given? ? yield(q) : q.cmd_tuples
|
|
519
|
-
ensure
|
|
520
|
-
q.clear
|
|
521
|
-
end
|
|
496
|
+
begin
|
|
497
|
+
block_given? ? yield(q) : q.cmd_tuples
|
|
498
|
+
ensure
|
|
499
|
+
q.clear
|
|
522
500
|
end
|
|
523
501
|
end
|
|
524
502
|
end
|
|
@@ -549,6 +527,10 @@ module Sequel
|
|
|
549
527
|
end
|
|
550
528
|
end
|
|
551
529
|
|
|
530
|
+
# Don't log, since logging is done by the underlying connection.
|
|
531
|
+
def log_connection_execute(conn, sql)
|
|
532
|
+
conn.execute(sql)
|
|
533
|
+
end
|
|
552
534
|
|
|
553
535
|
# If the value is an infinite value (either an infinite float or a string returned by
|
|
554
536
|
# by PostgreSQL for an infinite timestamp), return it without converting it if
|
|
@@ -573,6 +555,7 @@ module Sequel
|
|
|
573
555
|
include Sequel::Postgres::DatasetMethods
|
|
574
556
|
|
|
575
557
|
Database::DatasetClass = self
|
|
558
|
+
APOS = Sequel::Dataset::APOS
|
|
576
559
|
|
|
577
560
|
# Yield all rows returned by executing the given SQL and converting
|
|
578
561
|
# the types.
|
|
@@ -708,7 +691,7 @@ module Sequel
|
|
|
708
691
|
ps.extend(PreparedStatementMethods)
|
|
709
692
|
if name
|
|
710
693
|
ps.prepared_statement_name = name
|
|
711
|
-
db.
|
|
694
|
+
db.set_prepared_statement(name, ps)
|
|
712
695
|
end
|
|
713
696
|
ps
|
|
714
697
|
end
|
|
@@ -766,12 +749,12 @@ module Sequel
|
|
|
766
749
|
|
|
767
750
|
# Use the driver's escape_bytea
|
|
768
751
|
def literal_blob_append(sql, v)
|
|
769
|
-
sql <<
|
|
752
|
+
sql << APOS << db.synchronize{|c| c.escape_bytea(v)} << APOS
|
|
770
753
|
end
|
|
771
754
|
|
|
772
755
|
# Use the driver's escape_string
|
|
773
756
|
def literal_string_append(sql, v)
|
|
774
|
-
sql <<
|
|
757
|
+
sql << APOS << db.synchronize{|c| c.escape_string(v)} << APOS
|
|
775
758
|
end
|
|
776
759
|
|
|
777
760
|
# For each row in the result set, yield a hash with column name symbol
|
|
@@ -131,12 +131,24 @@ module Sequel
|
|
|
131
131
|
super
|
|
132
132
|
end
|
|
133
133
|
|
|
134
|
+
# Insert data from the current table into the new table after
|
|
135
|
+
# creating the table, since it is not possible to do it in one step.
|
|
136
|
+
def create_table_as(name, sql, options)
|
|
137
|
+
super
|
|
138
|
+
from(name).insert(sql.is_a?(Dataset) ? sql : dataset.with_sql(sql))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# DB2 requires parens around the SELECT, and DEFINITION ONLY at the end.
|
|
142
|
+
def create_table_as_sql(name, sql, options)
|
|
143
|
+
"#{create_table_prefix_sql(name, options)} AS (#{sql}) DEFINITION ONLY"
|
|
144
|
+
end
|
|
145
|
+
|
|
134
146
|
# Here we use DGTT which has most backward compatibility, which uses
|
|
135
147
|
# DECLARE instead of CREATE. CGTT can only be used after version 9.7.
|
|
136
148
|
# http://www.ibm.com/developerworks/data/library/techarticle/dm-0912globaltemptable/
|
|
137
|
-
def
|
|
149
|
+
def create_table_prefix_sql(name, options)
|
|
138
150
|
if options[:temp]
|
|
139
|
-
"DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)}
|
|
151
|
+
"DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)}"
|
|
140
152
|
else
|
|
141
153
|
super
|
|
142
154
|
end
|
|
@@ -221,6 +233,8 @@ module Sequel
|
|
|
221
233
|
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
|
|
222
234
|
when :>>
|
|
223
235
|
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
|
|
236
|
+
when :%
|
|
237
|
+
sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
|
|
224
238
|
when :'B~'
|
|
225
239
|
literal_append(sql, SQL::Function.new(:BITNOT, *args))
|
|
226
240
|
when :extract
|
|
@@ -72,6 +72,11 @@ module Sequel
|
|
|
72
72
|
true
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
# MSSQL supports transaction DDL statements.
|
|
76
|
+
def supports_transactional_ddl?
|
|
77
|
+
true
|
|
78
|
+
end
|
|
79
|
+
|
|
75
80
|
# Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
|
|
76
81
|
# information on tables.
|
|
77
82
|
def tables(opts={})
|
|
@@ -141,7 +146,7 @@ module Sequel
|
|
|
141
146
|
# Commit the active transaction on the connection, does not commit/release
|
|
142
147
|
# savepoints.
|
|
143
148
|
def commit_transaction(conn, opts={})
|
|
144
|
-
log_connection_execute(conn, commit_transaction_sql) unless
|
|
149
|
+
log_connection_execute(conn, commit_transaction_sql) unless _trans(conn)[:savepoint_level] > 1
|
|
145
150
|
end
|
|
146
151
|
|
|
147
152
|
# SQL to COMMIT a transaction.
|
|
@@ -152,10 +157,19 @@ module Sequel
|
|
|
152
157
|
# MSSQL uses the name of the table to decide the difference between
|
|
153
158
|
# a regular and temporary table, with temporary table names starting with
|
|
154
159
|
# a #.
|
|
155
|
-
def
|
|
156
|
-
"CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)}
|
|
160
|
+
def create_table_prefix_sql(name, options)
|
|
161
|
+
"CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)}"
|
|
157
162
|
end
|
|
158
163
|
|
|
164
|
+
# MSSQL doesn't support CREATE TABLE AS, it only supports SELECT INTO.
|
|
165
|
+
# Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
|
|
166
|
+
# is given as the argument, it can't work with a string, so raise an
|
|
167
|
+
# Error if a string is given.
|
|
168
|
+
def create_table_as(name, ds, options)
|
|
169
|
+
raise(Error, "must provide dataset instance as value of create_table :as option on MSSQL") unless ds.is_a?(Sequel::Dataset)
|
|
170
|
+
run(ds.into(name).sql)
|
|
171
|
+
end
|
|
172
|
+
|
|
159
173
|
# The name of the constraint for setting the default value on the table and column.
|
|
160
174
|
def default_constraint_name(table, column)
|
|
161
175
|
from(:sysobjects___c_obj).
|
|
@@ -325,12 +339,16 @@ module Sequel
|
|
|
325
339
|
OUTPUT_INSERTED = " OUTPUT INSERTED.*".freeze
|
|
326
340
|
HEX_START = '0x'.freeze
|
|
327
341
|
UNICODE_STRING_START = "N'".freeze
|
|
342
|
+
BACKSLASH_CRLF_RE = /\\((?:\r\n)|\n)/.freeze
|
|
343
|
+
BACKSLASH_CRLF_REPLACE = '\\\\\\\\\\1\\1'.freeze
|
|
328
344
|
TOP_PAREN = " TOP (".freeze
|
|
329
345
|
TOP = " TOP ".freeze
|
|
330
346
|
OUTPUT = " OUTPUT ".freeze
|
|
331
347
|
HSTAR = "H*".freeze
|
|
332
348
|
CASE_SENSITIVE_COLLATION = 'Latin1_General_CS_AS'.freeze
|
|
333
349
|
CASE_INSENSITIVE_COLLATION = 'Latin1_General_CI_AS'.freeze
|
|
350
|
+
DEFAULT_TIMESTAMP_FORMAT = "'%Y-%m-%dT%H:%M:%S%N%z'".freeze
|
|
351
|
+
FORMAT_DATE = "'%Y%m%d'".freeze
|
|
334
352
|
|
|
335
353
|
# Allow overriding of the mssql_unicode_strings option at the dataset level.
|
|
336
354
|
attr_accessor :mssql_unicode_strings
|
|
@@ -557,6 +575,13 @@ module Sequel
|
|
|
557
575
|
server_version >= 10000000
|
|
558
576
|
end
|
|
559
577
|
|
|
578
|
+
# Use strict ISO-8601 format with T between date and time,
|
|
579
|
+
# since that is the format that is multilanguage and not
|
|
580
|
+
# DATEFORMAT dependent.
|
|
581
|
+
def default_timestamp_format
|
|
582
|
+
DEFAULT_TIMESTAMP_FORMAT
|
|
583
|
+
end
|
|
584
|
+
|
|
560
585
|
# MSSQL supports the OUTPUT clause for DELETE statements.
|
|
561
586
|
# It also allows prepending a WITH clause.
|
|
562
587
|
def delete_clause_methods
|
|
@@ -612,18 +637,24 @@ module Sequel
|
|
|
612
637
|
sql << HEX_START << v.unpack(HSTAR).first
|
|
613
638
|
end
|
|
614
639
|
|
|
615
|
-
#
|
|
616
|
-
#
|
|
617
|
-
def
|
|
618
|
-
|
|
619
|
-
sql << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
640
|
+
# Use YYYYmmdd format, since that's the only want that is
|
|
641
|
+
# multilanguage and not DATEFORMAT dependent.
|
|
642
|
+
def literal_date(v)
|
|
643
|
+
v.strftime(FORMAT_DATE)
|
|
620
644
|
end
|
|
621
|
-
|
|
645
|
+
|
|
622
646
|
# Use 0 for false on MSSQL
|
|
623
647
|
def literal_false
|
|
624
648
|
BOOL_FALSE
|
|
625
649
|
end
|
|
626
650
|
|
|
651
|
+
# Optionally use unicode string syntax for all strings. Don't double
|
|
652
|
+
# backslashes.
|
|
653
|
+
def literal_string_append(sql, v)
|
|
654
|
+
sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
|
|
655
|
+
sql << v.gsub(APOS_RE, DOUBLE_APOS).gsub(BACKSLASH_CRLF_RE, BACKSLASH_CRLF_REPLACE) << APOS
|
|
656
|
+
end
|
|
657
|
+
|
|
627
658
|
# Use 1 for true on MSSQL
|
|
628
659
|
def literal_true
|
|
629
660
|
BOOL_TRUE
|
|
@@ -223,7 +223,7 @@ module Sequel
|
|
|
223
223
|
# Use XA START to start a new prepared transaction if the :prepare
|
|
224
224
|
# option is given.
|
|
225
225
|
def begin_transaction(conn, opts={})
|
|
226
|
-
if (s = opts[:prepare]) && (th =
|
|
226
|
+
if (s = opts[:prepare]) && (th = _trans(conn))[:savepoint_level] == 0
|
|
227
227
|
log_connection_execute(conn, "XA START #{literal(s)}")
|
|
228
228
|
th[:savepoint_level] += 1
|
|
229
229
|
else
|
|
@@ -246,7 +246,7 @@ module Sequel
|
|
|
246
246
|
# Prepare the XA transaction for a two-phase commit if the
|
|
247
247
|
# :prepare option is given.
|
|
248
248
|
def commit_transaction(conn, opts={})
|
|
249
|
-
if (s = opts[:prepare]) &&
|
|
249
|
+
if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
|
|
250
250
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
251
251
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
252
252
|
else
|
|
@@ -335,7 +335,7 @@ module Sequel
|
|
|
335
335
|
|
|
336
336
|
# Rollback the currently open XA transaction
|
|
337
337
|
def rollback_transaction(conn, opts={})
|
|
338
|
-
if (s = opts[:prepare]) &&
|
|
338
|
+
if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
|
|
339
339
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
340
340
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
341
341
|
log_connection_execute(conn, "XA ROLLBACK #{literal(s)}")
|
|
@@ -353,7 +353,9 @@ module Sequel
|
|
|
353
353
|
def schema_parse_table(table_name, opts)
|
|
354
354
|
m = output_identifier_meth(opts[:dataset])
|
|
355
355
|
im = input_identifier_meth(opts[:dataset])
|
|
356
|
-
|
|
356
|
+
table = SQL::Identifier.new(im.call(table_name))
|
|
357
|
+
table = SQL::QualifiedIdentifier.new(im.call(opts[:schema]), table) if opts[:schema]
|
|
358
|
+
metadata_dataset.with_sql("DESCRIBE ?", table).map do |row|
|
|
357
359
|
row[:auto_increment] = true if row.delete(:Extra).to_s =~ /auto_increment/io
|
|
358
360
|
row[:allow_null] = row.delete(:Null) == 'YES'
|
|
359
361
|
row[:default] = row.delete(:Default)
|
|
@@ -414,6 +416,9 @@ module Sequel
|
|
|
414
416
|
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert ignore into columns values on_duplicate_key_update')
|
|
415
417
|
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
|
|
416
418
|
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update ignore table set where order limit')
|
|
419
|
+
APOS = Dataset::APOS
|
|
420
|
+
APOS_RE = Dataset::APOS_RE
|
|
421
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
417
422
|
SPACE = Dataset::SPACE
|
|
418
423
|
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
419
424
|
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
@@ -446,6 +451,8 @@ module Sequel
|
|
|
446
451
|
MATCH_AGAINST_BOOLEAN = ["(MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE))".freeze].freeze
|
|
447
452
|
EXPLAIN = 'EXPLAIN '.freeze
|
|
448
453
|
EXPLAIN_EXTENDED = 'EXPLAIN EXTENDED '.freeze
|
|
454
|
+
BACKSLASH_RE = /\\/.freeze
|
|
455
|
+
QUAD_BACKSLASH = "\\\\\\\\".freeze
|
|
449
456
|
|
|
450
457
|
# MySQL specific syntax for LIKE/REGEXP searches, as well as
|
|
451
458
|
# string concatenation.
|
|
@@ -772,6 +779,11 @@ module Sequel
|
|
|
772
779
|
BOOL_FALSE
|
|
773
780
|
end
|
|
774
781
|
|
|
782
|
+
# SQL fragment for String. Doubles \ and ' by default.
|
|
783
|
+
def literal_string_append(sql, v)
|
|
784
|
+
sql << APOS << v.gsub(BACKSLASH_RE, QUAD_BACKSLASH).gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
785
|
+
end
|
|
786
|
+
|
|
775
787
|
# Use 1 for true on MySQL
|
|
776
788
|
def literal_true
|
|
777
789
|
BOOL_TRUE
|