Skip to content

Commit 7650860

Browse files
authored
Fix composite primary key with different data type with triggers (#1164)
1 parent 73382f3 commit 7650860

File tree

5 files changed

+50
-5
lines changed

5 files changed

+50
-5
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ adapter.exclude_output_inserted_table_names['my_table_name'] = true
5050

5151
# Explicitly set the data type for the temporary key table.
5252
adapter.exclude_output_inserted_table_names['my_uuid_table_name'] = 'uniqueidentifier'
53+
54+
55+
# Explicitly set data types when data type is different for composite primary keys.
56+
adapter.exclude_output_inserted_table_names['my_composite_pk_table_name'] = { pk_col_one: "uniqueidentifier", pk_col_two: "int" }
5357
```
5458

5559

lib/active_record/connection_adapters/sqlserver/database_statements.rb

+15-5
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,17 @@ def sql_for_insert(sql, pk, binds, returning)
278278
exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql)
279279

280280
if exclude_output_inserted
281-
quoted_pk = Array(pk).map { |subkey| SQLServer::Utils.extract_identifiers(subkey).quoted }
281+
pk_and_types = Array(pk).map do |subkey|
282+
{
283+
quoted: SQLServer::Utils.extract_identifiers(subkey).quoted,
284+
id_sql_type: exclude_output_inserted_id_sql_type(subkey, exclude_output_inserted)
285+
}
286+
end
282287

283-
id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted
284288
<<~SQL.squish
285-
DECLARE @ssaIdInsertTable table (#{quoted_pk.map { |subkey| "#{subkey} #{id_sql_type}"}.join(", ") });
286-
#{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT #{ quoted_pk.map { |subkey| "INSERTED.#{subkey}" }.join(", ") } INTO @ssaIdInsertTable"}
287-
SELECT #{quoted_pk.map {|subkey| "CAST(#{subkey} AS #{id_sql_type}) #{subkey}"}.join(", ")} FROM @ssaIdInsertTable
289+
DECLARE @ssaIdInsertTable table (#{pk_and_types.map { |pk_and_type| "#{pk_and_type[:quoted]} #{pk_and_type[:id_sql_type]}"}.join(", ") });
290+
#{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT #{ pk_and_types.map { |pk_and_type| "INSERTED.#{pk_and_type[:quoted]}" }.join(", ") } INTO @ssaIdInsertTable"}
291+
SELECT #{pk_and_types.map {|pk_and_type| "CAST(#{pk_and_type[:quoted]} AS #{pk_and_type[:id_sql_type]}) #{pk_and_type[:quoted]}"}.join(", ")} FROM @ssaIdInsertTable
288292
SQL
289293
else
290294
returning_columns = returning || Array(pk)
@@ -385,6 +389,12 @@ def exclude_output_inserted_table_name?(table_name, sql)
385389
self.class.exclude_output_inserted_table_names[table_name]
386390
end
387391

392+
def exclude_output_inserted_id_sql_type(pk, exclude_output_inserted)
393+
return "bigint" if exclude_output_inserted.is_a?(TrueClass)
394+
return exclude_output_inserted[pk.to_sym] if exclude_output_inserted.is_a?(Hash)
395+
exclude_output_inserted
396+
end
397+
388398
def query_requires_identity_insert?(sql)
389399
return false unless insert_sql?(sql)
390400

test/cases/trigger_test_sqlserver.rb

+10
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,14 @@ class SQLServerTriggerTest < ActiveRecord::TestCase
3838
_(obj.pk_col_two).must_equal 42
3939
_(obj.pk_col_one.to_s).must_equal SSTestTriggerHistory.first.id_source
4040
end
41+
42+
it "can insert into a table with composite pk with different data type with output inserted - with a hash setting for table name" do
43+
exclude_output_inserted_table_names["sst_table_with_composite_pk_trigger_with_different_data_type"] = { pk_col_one: "uniqueidentifier", pk_col_two: "int" }
44+
assert SSTestTriggerHistory.all.empty?
45+
obj = SSTestTriggerCompositePkWithDefferentDataType.create! pk_col_two: 123, event_name: "test trigger"
46+
_(obj.event_name).must_equal "test trigger"
47+
_(obj.pk_col_one).must_be :present?
48+
_(obj.pk_col_two).must_equal 123
49+
_(obj.pk_col_one.to_s).must_equal SSTestTriggerHistory.first.id_source
50+
end
4151
end

test/models/sqlserver/trigger.rb

+4
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ class SSTestTriggerUuid < ActiveRecord::Base
1111
class SSTestTriggerCompositePk < ActiveRecord::Base
1212
self.table_name = "sst_table_with_composite_pk_trigger"
1313
end
14+
15+
class SSTestTriggerCompositePkWithDefferentDataType < ActiveRecord::Base
16+
self.table_name = "sst_table_with_composite_pk_trigger_with_different_data_type"
17+
end

test/schema/sqlserver_specific_schema.rb

+17
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,23 @@
249249
SELECT pk_col_one AS id_source, event_name FROM INSERTED
250250
SQL
251251

252+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_composite_pk_trigger_with_different_data_type') DROP TABLE sst_table_with_composite_pk_trigger_with_different_data_type"
253+
execute <<-SQL
254+
CREATE TABLE sst_table_with_composite_pk_trigger_with_different_data_type(
255+
pk_col_one uniqueidentifier DEFAULT NEWID(),
256+
pk_col_two int NOT NULL,
257+
event_name nvarchar(255),
258+
CONSTRAINT PK_sst_table_with_composite_pk_trigger_with_different_data_type PRIMARY KEY (pk_col_one, pk_col_two)
259+
)
260+
SQL
261+
execute <<-SQL
262+
CREATE TRIGGER sst_table_with_composite_pk_trigger_with_different_data_type_t ON sst_table_with_composite_pk_trigger_with_different_data_type
263+
FOR INSERT
264+
AS
265+
INSERT INTO sst_table_with_trigger_history (id_source, event_name)
266+
SELECT pk_col_one AS id_source, event_name FROM INSERTED
267+
SQL
268+
252269
# Another schema.
253270

254271
create_table :sst_schema_columns, force: true do |t|

0 commit comments

Comments
 (0)