Skip to content

Commit f264abd

Browse files
committed
Do not ues Regexp for ID insert guards. For example:
```ruby c = Company.connection c.with_identity_insert_enabled('companies') do c.insert "INSERT INTO companies (id, name) VALUES(100, N'Totally happening!')" end ```
1 parent 1c35043 commit f264abd

File tree

5 files changed

+50
-27
lines changed

5 files changed

+50
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* Simple 2012 schmea addition and extensive column/type_cast object tests.
1818
* Follow Rails convention and remove varying character default limits.
1919
* The `cs_equality_operator` is now s class configuration property only.
20+
* The `with_identity_insert_enabled(table_name)` is now public.
2021

2122
#### Deprecated
2223

@@ -38,6 +39,7 @@
3839
* edition
3940
* Removed tests for old issue #164. Handled by core types now.
4041
* The `activity_stats` method. Please put this in a gem if needed.
42+
* We no longger use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper.
4143

4244
#### Fixed
4345

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@ def execute(sql, name = nil)
1616
end
1717

1818
def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {})
19-
if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil
20-
with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) }
21-
elsif update_sql?(sql)
19+
if update_sql?(sql)
2220
sql = strip_ident_from_update(sql)
2321
do_exec_query(sql, name, binds)
2422
else
2523
do_exec_query(sql, name, binds)
2624
end
2725
end
2826

29-
# The abstract adapter ignores the last two parameters also
3027
def exec_insert(sql, name, binds, _pk = nil, _sequence_name = nil)
31-
exec_query sql, name, binds, insert: true
28+
id_insert = binds_have_identity_column?(binds)
29+
id_table = table_name_from_binds(binds) if id_insert
30+
if id_insert && id_table
31+
with_identity_insert_enabled(id_table) { exec_query(sql, name, binds) }
32+
else
33+
exec_query(sql, name, binds)
34+
end
3235
end
3336

3437
def exec_delete(sql, name, binds)
@@ -110,6 +113,14 @@ def execute_procedure(proc_name, *variables)
110113
end
111114
end
112115

116+
def with_identity_insert_enabled(table_name)
117+
table_name = quote_table_name(table_name_or_views_table_name(table_name))
118+
set_identity_insert(table_name, true)
119+
yield
120+
ensure
121+
set_identity_insert(table_name, false)
122+
end
123+
113124
def use_database(database = nil)
114125
return if sqlserver_azure?
115126
name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted
@@ -269,6 +280,26 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
269280

270281
# === SQLServer Specific ======================================== #
271282

283+
def binds_have_identity_column?(binds)
284+
binds.any? do |column_value|
285+
column, value = column_value
286+
SQLServerColumn === column && column.is_identity?
287+
end
288+
end
289+
290+
def table_name_from_binds(binds)
291+
binds.detect { |column_value|
292+
column, value = column_value
293+
SQLServerColumn === column
294+
}.try(:first).try(:table_name)
295+
end
296+
297+
def set_identity_insert(table_name, enable = true)
298+
do_execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
299+
rescue Exception
300+
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
301+
end
302+
272303
def valid_isolation_levels
273304
['READ COMMITTED', 'READ UNCOMMITTED', 'REPEATABLE READ', 'SERIALIZABLE', 'SNAPSHOT']
274305
end

lib/active_record/connection_adapters/sqlserver/schema_statements.rb

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ def views
156156
tables('VIEW')
157157
end
158158

159+
159160
protected
160161

161162
# === SQLServer Specific ======================================== #
@@ -421,21 +422,6 @@ def update_sql?(sql)
421422
!(sql =~ /^\s*(UPDATE|EXEC sp_executesql N'UPDATE)/i).nil?
422423
end
423424

424-
def with_identity_insert_enabled(table_name)
425-
table_name = quote_table_name(table_name_or_views_table_name(table_name))
426-
set_identity_insert(table_name, true)
427-
yield
428-
ensure
429-
set_identity_insert(table_name, false)
430-
end
431-
432-
def set_identity_insert(table_name, enable = true)
433-
sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
434-
do_execute sql, 'SCHEMA'
435-
rescue Exception
436-
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
437-
end
438-
439425
def identity_column(table_name)
440426
schema_cache.columns(table_name).find(&:is_identity?)
441427
end

test/cases/adapter_test_sqlserver.rb

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -446,14 +446,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
446446

447447
# Doing identity inserts
448448

449-
let(:view_insert_sql) { "INSERT INTO [sst_customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)" }
450-
451-
it 'respond true/tablename to #query_requires_identity_insert?' do
452-
assert_equal '[sst_customers_view]', connection.send(:query_requires_identity_insert?, view_insert_sql)
453-
end
454-
455449
it 'be able to do an identity insert' do
456-
assert_nothing_raised { connection.execute( view_insert_sql) }
450+
customer = SSTestCustomersView.new
451+
customer.id = 420
452+
customer.save!
457453
assert SSTestCustomersView.find(420)
458454
end
459455

test/cases/coerced_tests.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ def test_not_eq_with_array_parameter_coerced
7070
require 'models/company'
7171
class InheritanceTest < ActiveRecord::TestCase
7272

73+
coerce_tests! :test_a_bad_type_column
74+
def test_a_bad_type_column_coerced
75+
Company.connection.with_identity_insert_enabled('companies') do
76+
Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
77+
end
78+
assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
79+
end
80+
7381
coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
7482
def test_eager_load_belongs_to_primary_key_quoting_coerced
7583
con = Account.connection

0 commit comments

Comments
 (0)