activerecord-sqlserver-adapter 7.0.5.1 → 7.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -3
- data/.gitignore +3 -1
- data/CHANGELOG.md +38 -83
- data/Gemfile +3 -0
- data/README.md +16 -11
- data/RUNNING_UNIT_TESTS.md +24 -10
- data/Rakefile +2 -6
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +29 -6
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +15 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -31
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +86 -133
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +3 -2
- data/lib/active_record/connection_adapters/sqlserver/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +68 -29
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +6 -0
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +10 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +81 -114
- data/lib/active_record/connection_adapters/sqlserver_column.rb +1 -0
- data/lib/active_record/sqlserver_base.rb +1 -10
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +5 -2
- data/lib/arel/visitors/sqlserver.rb +0 -33
- data/test/cases/adapter_test_sqlserver.rb +8 -7
- data/test/cases/coerced_tests.rb +526 -235
- data/test/cases/column_test_sqlserver.rb +6 -6
- data/test/cases/connection_test_sqlserver.rb +3 -6
- data/test/cases/disconnected_test_sqlserver.rb +5 -8
- data/test/cases/execute_procedure_test_sqlserver.rb +1 -1
- data/test/cases/rake_test_sqlserver.rb +1 -1
- data/test/cases/schema_dumper_test_sqlserver.rb +2 -2
- data/test/cases/transaction_test_sqlserver.rb +13 -8
- data/test/config.yml +1 -2
- data/test/support/connection_reflection.rb +2 -8
- data/test/support/core_ext/query_cache.rb +7 -1
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump +0 -0
- data/test/support/sql_counter_sqlserver.rb +0 -15
- metadata +14 -8
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module SQLServer
|
|
6
6
|
module DatabaseStatements
|
|
7
|
-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback, :waitfor) # :nodoc:
|
|
7
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback, :waitfor, :use) # :nodoc:
|
|
8
8
|
private_constant :READ_QUERY
|
|
9
9
|
|
|
10
10
|
def write_query?(sql) # :nodoc:
|
|
@@ -13,40 +13,54 @@ module ActiveRecord
|
|
|
13
13
|
!READ_QUERY.match?(sql.b)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
if preventing_writes? && write_query?(sql)
|
|
19
|
-
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
|
20
|
-
end
|
|
16
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
|
17
|
+
result = nil
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
log(sql, name, async: async) do
|
|
20
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
21
|
+
result = if id_insert_table_name = query_requires_identity_insert?(sql)
|
|
22
|
+
with_identity_insert_enabled(id_insert_table_name, conn) { internal_raw_execute(sql, conn, perform_do: true) }
|
|
23
|
+
else
|
|
24
|
+
internal_raw_execute(sql, conn, perform_do: true)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
29
27
|
end
|
|
28
|
+
|
|
29
|
+
result
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
def
|
|
32
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false)
|
|
33
|
+
result = nil
|
|
33
34
|
sql = transform_query(sql)
|
|
34
|
-
if preventing_writes? && write_query?(sql)
|
|
35
|
-
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
|
36
|
-
end
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
check_if_write_query(sql)
|
|
39
37
|
mark_transaction_written_if_write(sql)
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
unless without_prepared_statement?(binds)
|
|
40
|
+
types, params = sp_executesql_types_and_parameters(binds)
|
|
41
|
+
sql = sp_executesql_sql(sql, types, params, name)
|
|
42
|
+
end
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
log(sql, name, binds, async: async) do
|
|
45
|
+
with_raw_connection do |conn|
|
|
46
|
+
if id_insert_table_name = query_requires_identity_insert?(sql)
|
|
47
|
+
with_identity_insert_enabled(id_insert_table_name, conn) do
|
|
48
|
+
result = internal_exec_sql_query(sql, conn)
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
result = internal_exec_sql_query(sql, conn)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
49
54
|
end
|
|
55
|
+
|
|
56
|
+
result
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def internal_exec_sql_query(sql, conn)
|
|
60
|
+
handle = internal_raw_execute(sql, conn)
|
|
61
|
+
handle_to_names_and_values(handle, ar_result: true)
|
|
62
|
+
ensure
|
|
63
|
+
finish_statement_handle(handle)
|
|
50
64
|
end
|
|
51
65
|
|
|
52
66
|
def exec_delete(sql, name, binds)
|
|
@@ -60,7 +74,7 @@ module ActiveRecord
|
|
|
60
74
|
end
|
|
61
75
|
|
|
62
76
|
def begin_db_transaction
|
|
63
|
-
|
|
77
|
+
internal_execute("BEGIN TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
64
78
|
end
|
|
65
79
|
|
|
66
80
|
def transaction_isolation_levels
|
|
@@ -68,33 +82,20 @@ module ActiveRecord
|
|
|
68
82
|
end
|
|
69
83
|
|
|
70
84
|
def begin_isolated_db_transaction(isolation)
|
|
71
|
-
set_transaction_isolation_level
|
|
85
|
+
set_transaction_isolation_level(transaction_isolation_levels.fetch(isolation))
|
|
72
86
|
begin_db_transaction
|
|
73
87
|
end
|
|
74
88
|
|
|
75
89
|
def set_transaction_isolation_level(isolation_level)
|
|
76
|
-
|
|
90
|
+
internal_execute("SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
77
91
|
end
|
|
78
92
|
|
|
79
93
|
def commit_db_transaction
|
|
80
|
-
|
|
94
|
+
internal_execute("COMMIT TRANSACTION", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
81
95
|
end
|
|
82
96
|
|
|
83
97
|
def exec_rollback_db_transaction
|
|
84
|
-
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
include Savepoints
|
|
88
|
-
|
|
89
|
-
def create_savepoint(name = current_savepoint_name)
|
|
90
|
-
do_execute "SAVE TRANSACTION #{name}", "TRANSACTION"
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def exec_rollback_to_savepoint(name = current_savepoint_name)
|
|
94
|
-
do_execute "ROLLBACK TRANSACTION #{name}", "TRANSACTION"
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def release_savepoint(name = current_savepoint_name)
|
|
98
|
+
internal_execute("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
98
99
|
end
|
|
99
100
|
|
|
100
101
|
def case_sensitive_comparison(attribute, value)
|
|
@@ -164,42 +165,42 @@ module ActiveRecord
|
|
|
164
165
|
# === SQLServer Specific ======================================== #
|
|
165
166
|
|
|
166
167
|
def execute_procedure(proc_name, *variables)
|
|
167
|
-
materialize_transactions
|
|
168
|
-
|
|
169
168
|
vars = if variables.any? && variables.first.is_a?(Hash)
|
|
170
169
|
variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
|
|
171
170
|
else
|
|
172
171
|
variables.map { |v| quote(v) }
|
|
173
172
|
end.join(", ")
|
|
174
173
|
sql = "EXEC #{proc_name} #{vars}".strip
|
|
175
|
-
|
|
176
|
-
log(sql,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
result = ensure_established_connection! { dblib_execute(sql) }
|
|
174
|
+
|
|
175
|
+
log(sql, "Execute Procedure") do
|
|
176
|
+
with_raw_connection do |conn|
|
|
177
|
+
result = internal_raw_execute(sql, conn)
|
|
180
178
|
options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc }
|
|
179
|
+
|
|
181
180
|
result.each(options) do |row|
|
|
182
181
|
r = row.with_indifferent_access
|
|
183
182
|
yield(r) if block_given?
|
|
184
183
|
end
|
|
184
|
+
|
|
185
185
|
result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row }
|
|
186
186
|
end
|
|
187
187
|
end
|
|
188
|
+
|
|
188
189
|
end
|
|
189
190
|
|
|
190
|
-
def with_identity_insert_enabled(table_name)
|
|
191
|
+
def with_identity_insert_enabled(table_name, conn)
|
|
191
192
|
table_name = quote_table_name(table_name)
|
|
192
|
-
set_identity_insert(table_name, true)
|
|
193
|
+
set_identity_insert(table_name, conn, true)
|
|
193
194
|
yield
|
|
194
195
|
ensure
|
|
195
|
-
set_identity_insert(table_name, false)
|
|
196
|
+
set_identity_insert(table_name, conn, false)
|
|
196
197
|
end
|
|
197
198
|
|
|
198
199
|
def use_database(database = nil)
|
|
199
200
|
return if sqlserver_azure?
|
|
200
201
|
|
|
201
|
-
name = SQLServer::Utils.extract_identifiers(database || @
|
|
202
|
-
|
|
202
|
+
name = SQLServer::Utils.extract_identifiers(database || @connection_parameters[:database]).quoted
|
|
203
|
+
execute("USE #{name}", "SCHEMA") unless name.blank?
|
|
203
204
|
end
|
|
204
205
|
|
|
205
206
|
def user_options
|
|
@@ -263,18 +264,19 @@ module ActiveRecord
|
|
|
263
264
|
|
|
264
265
|
protected
|
|
265
266
|
|
|
266
|
-
def sql_for_insert(sql, pk, binds)
|
|
267
|
+
def sql_for_insert(sql, pk, binds, returning)
|
|
267
268
|
if pk.nil?
|
|
268
269
|
table_name = query_requires_identity_insert?(sql)
|
|
269
270
|
pk = primary_key(table_name)
|
|
270
271
|
end
|
|
271
272
|
|
|
272
273
|
sql = if pk && use_output_inserted? && !database_prefix_remote_server?
|
|
273
|
-
quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
|
|
274
274
|
table_name ||= get_table_name(sql)
|
|
275
275
|
exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql)
|
|
276
276
|
|
|
277
277
|
if exclude_output_inserted
|
|
278
|
+
quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
|
|
279
|
+
|
|
278
280
|
id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted
|
|
279
281
|
<<~SQL.squish
|
|
280
282
|
DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type});
|
|
@@ -282,40 +284,32 @@ module ActiveRecord
|
|
|
282
284
|
SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable
|
|
283
285
|
SQL
|
|
284
286
|
else
|
|
285
|
-
|
|
287
|
+
returning_columns = returning || Array(pk)
|
|
288
|
+
|
|
289
|
+
if returning_columns.any?
|
|
290
|
+
returning_columns_statements = returning_columns.map { |c| " INSERTED.#{SQLServer::Utils.extract_identifiers(c).quoted}" }
|
|
291
|
+
sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT" + returning_columns_statements.join(",")
|
|
292
|
+
else
|
|
293
|
+
sql
|
|
294
|
+
end
|
|
286
295
|
end
|
|
287
296
|
else
|
|
288
297
|
"#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
|
289
298
|
end
|
|
290
|
-
|
|
299
|
+
|
|
300
|
+
[sql, binds]
|
|
291
301
|
end
|
|
292
302
|
|
|
293
303
|
# === SQLServer Specific ======================================== #
|
|
294
304
|
|
|
295
|
-
def set_identity_insert(table_name, enable
|
|
296
|
-
|
|
305
|
+
def set_identity_insert(table_name, conn, enable)
|
|
306
|
+
internal_raw_execute("SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}", conn , perform_do: true)
|
|
297
307
|
rescue Exception
|
|
298
308
|
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
|
|
299
309
|
end
|
|
300
310
|
|
|
301
311
|
# === SQLServer Specific (Executing) ============================ #
|
|
302
312
|
|
|
303
|
-
def do_execute(sql, name = "SQL")
|
|
304
|
-
materialize_transactions
|
|
305
|
-
mark_transaction_written_if_write(sql)
|
|
306
|
-
|
|
307
|
-
log(sql, name) { raw_connection_do(sql) }
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
def sp_executesql(sql, name, binds, options = {})
|
|
311
|
-
options[:ar_result] = true if options[:fetch] != :rows
|
|
312
|
-
unless without_prepared_statement?(binds)
|
|
313
|
-
types, params = sp_executesql_types_and_parameters(binds)
|
|
314
|
-
sql = sp_executesql_sql(sql, types, params, name)
|
|
315
|
-
end
|
|
316
|
-
raw_select sql, name, binds, options
|
|
317
|
-
end
|
|
318
|
-
|
|
319
313
|
def sp_executesql_types_and_parameters(binds)
|
|
320
314
|
types, params = [], []
|
|
321
315
|
binds.each_with_index do |attr, index|
|
|
@@ -328,6 +322,7 @@ module ActiveRecord
|
|
|
328
322
|
end
|
|
329
323
|
|
|
330
324
|
def sp_executesql_sql_type(attr)
|
|
325
|
+
return "nvarchar(max)".freeze if attr.is_a?(Symbol)
|
|
331
326
|
return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type)
|
|
332
327
|
|
|
333
328
|
case value = attr.value_for_database
|
|
@@ -339,6 +334,8 @@ module ActiveRecord
|
|
|
339
334
|
end
|
|
340
335
|
|
|
341
336
|
def sp_executesql_sql_param(attr)
|
|
337
|
+
return quote(attr) if attr.is_a?(Symbol)
|
|
338
|
+
|
|
342
339
|
case value = attr.value_for_database
|
|
343
340
|
when Type::Binary::Data,
|
|
344
341
|
ActiveRecord::Type::SQLServer::Data
|
|
@@ -363,16 +360,6 @@ module ActiveRecord
|
|
|
363
360
|
sql.freeze
|
|
364
361
|
end
|
|
365
362
|
|
|
366
|
-
def raw_connection_do(sql)
|
|
367
|
-
case @connection_options[:mode]
|
|
368
|
-
when :dblib
|
|
369
|
-
result = ensure_established_connection! { dblib_execute(sql) }
|
|
370
|
-
result.do
|
|
371
|
-
end
|
|
372
|
-
ensure
|
|
373
|
-
@update_sql = false
|
|
374
|
-
end
|
|
375
|
-
|
|
376
363
|
# === SQLServer Specific (Identity Inserts) ===================== #
|
|
377
364
|
|
|
378
365
|
def use_output_inserted?
|
|
@@ -392,10 +379,6 @@ module ActiveRecord
|
|
|
392
379
|
self.class.exclude_output_inserted_table_names[table_name]
|
|
393
380
|
end
|
|
394
381
|
|
|
395
|
-
def exec_insert_requires_identity?(sql, _pk, _binds)
|
|
396
|
-
query_requires_identity_insert?(sql)
|
|
397
|
-
end
|
|
398
|
-
|
|
399
382
|
def query_requires_identity_insert?(sql)
|
|
400
383
|
return false unless insert_sql?(sql)
|
|
401
384
|
|
|
@@ -415,68 +398,38 @@ module ActiveRecord
|
|
|
415
398
|
|
|
416
399
|
# === SQLServer Specific (Selecting) ============================ #
|
|
417
400
|
|
|
418
|
-
def
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
def _raw_select(sql, options = {})
|
|
423
|
-
handle = raw_connection_run(sql)
|
|
424
|
-
handle_to_names_and_values(handle, options)
|
|
401
|
+
def _raw_select(sql, conn)
|
|
402
|
+
handle = internal_raw_execute(sql, conn)
|
|
403
|
+
handle_to_names_and_values(handle, fetch: :rows)
|
|
425
404
|
ensure
|
|
426
405
|
finish_statement_handle(handle)
|
|
427
406
|
end
|
|
428
407
|
|
|
429
|
-
def raw_connection_run(sql)
|
|
430
|
-
case @connection_options[:mode]
|
|
431
|
-
when :dblib
|
|
432
|
-
ensure_established_connection! { dblib_execute(sql) }
|
|
433
|
-
end
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
def handle_more_results?(handle)
|
|
437
|
-
case @connection_options[:mode]
|
|
438
|
-
when :dblib
|
|
439
|
-
end
|
|
440
|
-
end
|
|
441
|
-
|
|
442
408
|
def handle_to_names_and_values(handle, options = {})
|
|
443
|
-
case @connection_options[:mode]
|
|
444
|
-
when :dblib
|
|
445
|
-
handle_to_names_and_values_dblib(handle, options)
|
|
446
|
-
end
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
def handle_to_names_and_values_dblib(handle, options = {})
|
|
450
409
|
query_options = {}.tap do |qo|
|
|
451
410
|
qo[:timezone] = ActiveRecord.default_timezone || :utc
|
|
452
411
|
qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash
|
|
453
412
|
end
|
|
454
413
|
results = handle.each(query_options)
|
|
455
414
|
columns = lowercase_schema_reflection ? handle.fields.map { |c| c.downcase } : handle.fields
|
|
415
|
+
|
|
456
416
|
options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results
|
|
457
417
|
end
|
|
458
418
|
|
|
459
419
|
def finish_statement_handle(handle)
|
|
460
|
-
|
|
461
|
-
when :dblib
|
|
462
|
-
handle.cancel if handle
|
|
463
|
-
end
|
|
420
|
+
handle.cancel if handle
|
|
464
421
|
handle
|
|
465
422
|
end
|
|
466
423
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
raise TinyTds::Error, "failed to execute statement" if
|
|
424
|
+
# TinyTDS returns false instead of raising an exception if connection fails.
|
|
425
|
+
# Getting around this by raising an exception ourselves while PR
|
|
426
|
+
# https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
|
|
427
|
+
def internal_raw_execute(sql, conn, perform_do: false)
|
|
428
|
+
result = conn.execute(sql).tap do |_result|
|
|
429
|
+
raise TinyTds::Error, "failed to execute statement" if _result.is_a?(FalseClass)
|
|
473
430
|
end
|
|
474
|
-
end
|
|
475
431
|
|
|
476
|
-
|
|
477
|
-
raise TinyTds::Error, 'SQL Server client is not connected' unless @connection
|
|
478
|
-
|
|
479
|
-
yield
|
|
432
|
+
perform_do ? result.do : result
|
|
480
433
|
end
|
|
481
434
|
end
|
|
482
435
|
end
|
|
@@ -8,13 +8,13 @@ module ActiveRecord
|
|
|
8
8
|
name = SQLServer::Utils.extract_identifiers(database)
|
|
9
9
|
db_options = create_database_options(options)
|
|
10
10
|
edition_options = create_database_edition_options(options)
|
|
11
|
-
|
|
11
|
+
execute "CREATE DATABASE #{name} #{db_options} #{edition_options}"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def drop_database(database)
|
|
15
15
|
name = SQLServer::Utils.extract_identifiers(database)
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
|
|
17
|
+
execute "DROP DATABASE #{name}"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def current_database
|
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
|
33
33
|
|
|
34
34
|
def create_database_options(options = {})
|
|
35
35
|
keys = [:collate]
|
|
36
|
-
copts = @
|
|
36
|
+
copts = @connection_parameters
|
|
37
37
|
options = {
|
|
38
38
|
collate: copts[:collation]
|
|
39
39
|
}.merge(options.symbolize_keys).select { |_, v|
|
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
|
46
46
|
|
|
47
47
|
def create_database_edition_options(options = {})
|
|
48
48
|
keys = [:maxsize, :edition, :service_objective]
|
|
49
|
-
copts = @
|
|
49
|
+
copts = @connection_parameters
|
|
50
50
|
edition_options = {
|
|
51
51
|
maxsize: copts[:azure_maxsize],
|
|
52
52
|
edition: copts[:azure_edition],
|
|
@@ -85,7 +85,7 @@ module ActiveRecord
|
|
|
85
85
|
(
|
|
86
86
|
(?:
|
|
87
87
|
# [database_name].[database_owner].[table_name].[column_name] | function(one or no argument)
|
|
88
|
-
((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])
|
|
88
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\))
|
|
89
89
|
)
|
|
90
90
|
(?:\s+AS\s+(?:\w+|\[\w+\]))?
|
|
91
91
|
)
|
|
@@ -98,8 +98,9 @@ module ActiveRecord
|
|
|
98
98
|
(
|
|
99
99
|
(?:
|
|
100
100
|
# [database_name].[database_owner].[table_name].[column_name] | function(one or no argument)
|
|
101
|
-
((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])
|
|
101
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\))
|
|
102
102
|
)
|
|
103
|
+
(?:\s+COLLATE\s+\w+)?
|
|
103
104
|
(?:\s+ASC|\s+DESC)?
|
|
104
105
|
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
|
105
106
|
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module SQLServer
|
|
6
|
+
module Savepoints
|
|
7
|
+
def current_savepoint_name
|
|
8
|
+
current_transaction.savepoint_name
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create_savepoint(name = current_savepoint_name)
|
|
12
|
+
internal_execute("SAVE TRANSACTION #{name}", "TRANSACTION")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def exec_rollback_to_savepoint(name = current_savepoint_name)
|
|
16
|
+
internal_execute("ROLLBACK TRANSACTION #{name}", "TRANSACTION")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def release_savepoint(_name)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|