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