rails-sqlserver / activerecord-sqlserver-adapter

SQL Server Adapter For Rails
MIT License
968 stars 558 forks source link

exclude_output_inserted_table_names does not support composite primary keys with different data types #1163

Closed vividmuimui closed 2 months ago

vividmuimui commented 2 months ago

Issue

The exclude_output_inserted_table_names feature does not handle composite primary keys with defferent data types properly.

Expected behavior

The exclude_output_inserted_table_names should be able to handle composite primary keys of different data types without errors.

Actual behavior

Currently, if you try to use exclude_output_inserted_table_names for a table with a composite primary key, this setting will not handle the scenario correctly. This limitation makes it particularly difficult to manage tables with composite keys containing different data types.

How to reproduce

Create a table with a composite primary key where the keys have different data types and attempt to insert data using the ActiveRecord-SQLServer adapter with exclude_output_inserted_table_names configured. Here is an example setup that triggers the error:

create_table "SampleTable", primary_key: ["KeyPart1", "KeyPart2"], force: :cascade do |t|
  t.uuid "KeyPart1", null: false
  t.integer "KeyPart2", null: false # different data type
end
adapter = ActiveRecord::ConnectionAdapters::SQLServerAdapter
adapter.exclude_output_inserted_table_names[SampleTable] = 'uniqueidentifier'

Attempting to insert data into this table results in the following error:

  TRANSACTION (0.7ms)  BEGIN TRANSACTION
  SampleTable Create (6.4ms)  EXEC sp_executesql N'DECLARE @ssaIdInsertTable table ([KeyPart1] uniqueidentifier, [KeyPart2] uniqueidentifier); INSERT INTO [SampleTable] ([KeyPart1], [KeyPart2], [CreatedAt], [UpdatedAt]) OUTPUT INSERTED.[KeyPart1], INSERTED.[KeyPart2] INTO @ssaIdInsertTable VALUES (@0, @1, @2, @3) SELECT CAST([KeyPart1] AS uniqueidentifier) [KeyPart1], CAST([KeyPart2] AS uniqueidentifier) [KeyPart2] FROM @ssaIdInsertTable', N'@0 uniqueidentifier, @1 int, @2 datetimeoffset(7), @3 datetimeoffset(7)', @0 = '4128767C-9775-4FC0-966E-757030073AD1', @1 = 1, @2 = '04-23-2024 09:21:01.189011', @3 = '04-23-2024 09:21:01.189011'  [["KeyPart1", nil], ["KeyPart2", nil], ["CreatedAt", nil], ["UpdatedAt", nil]]
  TRANSACTION (0.8ms)  IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION
/path/to/project/vendor/bundle/ruby/3.3.0/gems/activerecord-sqlserver-adapter-7.1.3/lib/active_record/connection_adapters/sqlserver/database_statements.rb:416:in `each': TinyTds::Error: Operand type clash: int is incompatible with uniqueidentifier (ActiveRecord::StatementInvalid)
/path/to/project/vendor/bundle/ruby/3.3.0/gems/activerecord-sqlserver-adapter-7.1.3/lib/active_record/connection_adapters/sqlserver/database_statements.rb:416:in `each': Operand type clash: int is incompatible with uniqueidentifier (TinyTds::Error)

Details

Compile-time settings (established with the "configure" script)
                            Version: freetds v1.4.12
             freetds.conf directory: /opt/homebrew/etc
     MS db-lib source compatibility: no
        Sybase binary compatibility: yes
                      Thread safety: yes
                      iconv library: yes
                        TDS version: 7.3
                              iODBC: no
                           unixodbc: yes
              SSPI "trusted" logins: no
                           Kerberos: yes
                            OpenSSL: yes
                             GnuTLS: no
                               MARS: yes