From 3cd27b85690af836c9d854e082fe7f80d3d978e7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 23 Aug 2024 11:30:31 +0100 Subject: [PATCH 01/33] Run CI against stable branch --- Dockerfile.ci | 2 +- docker-compose.ci.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 0ded95afd..f9437e97d 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_BRANCH=7-2-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index cbc842619..957b5ec9b 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,6 +5,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver + - RAILS_BRANCH=7-2-stable build: context: . dockerfile: Dockerfile.ci From c80d620f608446676284652a4a182d13648545b4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 4 Sep 2024 10:13:57 +0100 Subject: [PATCH 02/33] Run CI once a week --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1d3b0c80..65debeb86 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: pull_request: branches: [ main ] schedule: - - cron: '0 18 * * *' + - cron: '0 4 * * 2' jobs: test: From c3968ba13d74dc347de763bfe9b2c816a102f059 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 1 Oct 2024 13:55:28 +0100 Subject: [PATCH 03/33] Enable identity insert on view's base table --- CHANGELOG.md | 4 ++++ .../connection_adapters/sqlserver/database_statements.rb | 3 +++ test/cases/view_test_sqlserver.rb | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d04331d..b8ff9d03e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,8 @@ - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter +#### Fixed + +- [#](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/) Enable identity insert on view's base table + Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 741acb21e..009d79a01 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -42,6 +42,9 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa log(sql, name, binds, async: async) do |notification_payload| with_raw_connection do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) + # If the table name is a view, we need to get the base table name for enabling identity insert. + id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name) + with_identity_insert_enabled(id_insert_table_name, conn) do internal_exec_sql_query(sql, conn) end diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 73ddf006a..88ea6e091 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -47,4 +47,12 @@ class ViewTestSQLServer < ActiveRecord::TestCase assert_equal 1, klass.count end end + + describe 'identity insert' do + it "identity insert works with views" do + assert_difference("SSTestCustomersView.count", 1) do + SSTestCustomersView.create!(id: 5, name: "Bob") + end + end + end end From 644b51eeb32d2ffe174fd478b48f998b792961f6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 1 Oct 2024 14:00:43 +0100 Subject: [PATCH 04/33] Revert --- CHANGELOG.md | 4 ---- .../connection_adapters/sqlserver/database_statements.rb | 3 --- test/cases/view_test_sqlserver.rb | 8 -------- 3 files changed, 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8ff9d03e..65d04331d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,4 @@ - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter -#### Fixed - -- [#](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/) Enable identity insert on view's base table - Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 009d79a01..741acb21e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -42,9 +42,6 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa log(sql, name, binds, async: async) do |notification_payload| with_raw_connection do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) - # If the table name is a view, we need to get the base table name for enabling identity insert. - id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name) - with_identity_insert_enabled(id_insert_table_name, conn) do internal_exec_sql_query(sql, conn) end diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 88ea6e091..73ddf006a 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -47,12 +47,4 @@ class ViewTestSQLServer < ActiveRecord::TestCase assert_equal 1, klass.count end end - - describe 'identity insert' do - it "identity insert works with views" do - assert_difference("SSTestCustomersView.count", 1) do - SSTestCustomersView.create!(id: 5, name: "Bob") - end - end - end end From 193e7ef398d52a3d033769f283dbbfeea244c2af Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 1 Oct 2024 14:21:31 +0100 Subject: [PATCH 05/33] Enable identity insert on view's base table (#1231) --- .github/workflows/ci.yml | 10 ++-------- CHANGELOG.md | 4 ++++ .../sqlserver/database_statements.rb | 3 +++ test/cases/view_test_sqlserver.rb | 8 ++++++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65debeb86..1ec555c3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,17 +1,11 @@ name: CI -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - schedule: - - cron: '0 4 * * 2' +on: [push, pull_request] jobs: test: name: Run test suite - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 # TODO: Change back to 'ubuntu-latest' when https://github.com/microsoft/mssql-docker/issues/899 resolved. env: COMPOSE_FILE: docker-compose.ci.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d04331d..1218ff994 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,8 @@ - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter +#### Fixed + +- [#1231](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1231) Enable identity insert on view's base table + Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 741acb21e..009d79a01 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -42,6 +42,9 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa log(sql, name, binds, async: async) do |notification_payload| with_raw_connection do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) + # If the table name is a view, we need to get the base table name for enabling identity insert. + id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name) + with_identity_insert_enabled(id_insert_table_name, conn) do internal_exec_sql_query(sql, conn) end diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 73ddf006a..84bfd80e1 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -47,4 +47,12 @@ class ViewTestSQLServer < ActiveRecord::TestCase assert_equal 1, klass.count end end + + describe 'identity insert' do + it "identity insert works with views" do + assert_difference("SSTestCustomersView.count", 1) do + SSTestCustomersView.create!(id: 5, name: "Bob") + end + end + end end From a46f0dd0d1a4030956c9ba90bf113537c583393c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 2 Oct 2024 10:44:17 +0100 Subject: [PATCH 06/33] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1218ff994..54dfd9d1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.0 #### Added From bf1a324c9c119853aade257b58c92b83a2508e72 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 2 Oct 2024 10:45:09 +0100 Subject: [PATCH 07/33] Update CHANGELOG.md --- CHANGELOG.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54dfd9d1b..4354fed30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1231](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1231) Enable identity insert on view's base table + ## v7.2.0 #### Added @@ -9,8 +15,5 @@ - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter -#### Fixed - -- [#1231](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1231) Enable identity insert on view's base table Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. From 13349d996933091b6aed67a668b859acb2c8e2fc Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 2 Oct 2024 10:55:33 +0100 Subject: [PATCH 08/33] Release v7.2.1 --- CHANGELOG.md | 2 +- README.md | 4 ++-- VERSION | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4354fed30..b20c04d8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.1 #### Fixed diff --git a/README.md b/README.md index 2b8baf293..9b0e4f692 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ their stable branches. |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| | `7.2.x` | `7.2.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | -| `7.0.x` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.x` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | diff --git a/VERSION b/VERSION index 0ee843cc6..b26a34e47 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.0 +7.2.1 From 7fcf20d5f79be81a14429b1265c1511192be179c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 15 Oct 2024 11:09:41 +0100 Subject: [PATCH 09/33] Allow INSERT statements with SELECT notation (#1244) --- CHANGELOG.md | 6 ++++++ .../connection_adapters/sqlserver/schema_statements.rb | 1 + test/cases/schema_test_sqlserver.rb | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b20c04d8a..da0510f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation + ## v7.2.1 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index f85806478..29a172584 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -681,6 +681,7 @@ def get_raw_table_name(sql) s.split(/INSERT INTO/i)[1] .split(/OUTPUT INSERTED/i)[0] .split(/(DEFAULT)?\s+VALUES/i)[0] + .split(/\bSELECT\b(?![^\[]*\])/i)[0] .match(/\s*([^(]*)/i)[0] elsif s.match?(/^\s*UPDATE\s+.*/i) s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 255c58711..f9dbde8e6 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -96,6 +96,10 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it do assert_equal "[test].[aliens]", connection.send(:get_raw_table_name, "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name]) OUTPUT INSERTED.[id] VALUES (@0)', N'@0 varchar(255)', @0 = 'Trisolarans'") end + + it do + assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "INSERT INTO [with].[select notation] SELECT * FROM [table_name]") + end end end end From 63b6854298d7d32d2a2698392f87dc41af0341a4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 16 Oct 2024 10:44:36 +0100 Subject: [PATCH 10/33] Fix queries with date and date-time placeholder conditions (#1247) --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 14 ++++---------- test/cases/adapter_test_sqlserver.rb | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da0510f09..2a2e40f0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed - [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation +- [#1247](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1247) Fix queries with date and date-time placeholder conditions ## v7.2.1 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 009d79a01..64a3ed22b 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -339,7 +339,7 @@ def sp_executesql_sql_type(attr) end end - value = basic_attribute_type?(attr) ? attr : attr.value_for_database + value = active_model_attribute?(attr) ? attr.value_for_database : attr if value.is_a?(Numeric) value > 2_147_483_647 ? "bigint".freeze : "int".freeze @@ -349,7 +349,7 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) - return quote(attr) if basic_attribute_type?(attr) + return quote(attr) unless active_model_attribute?(attr) case value = attr.value_for_database when Type::Binary::Data, ActiveRecord::Type::SQLServer::Data @@ -359,14 +359,8 @@ def sp_executesql_sql_param(attr) end end - def basic_attribute_type?(type) - type.is_a?(Symbol) || - type.is_a?(String) || - type.is_a?(Numeric) || - type.is_a?(Time) || - type.is_a?(TrueClass) || - type.is_a?(FalseClass) || - type.is_a?(NilClass) + def active_model_attribute?(type) + type.is_a?(::ActiveModel::Attribute) end def sp_executesql_sql(sql, types, params, name) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 572f416e8..9921f23e6 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -580,4 +580,18 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end end + + describe "placeholder conditions" do + it 'using time placeholder' do + assert_equal Task.where("starting < ?", Time.now).count, 1 + end + + it 'using date placeholder' do + assert_equal Task.where("starting < ?", Date.today).count, 1 + end + + it 'using date-time placeholder' do + assert_equal Task.where("starting < ?", DateTime.current).count, 1 + end + end end From 111d33b72898c11dd2a4f6cbf02cef612151d417 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 22 Oct 2024 14:47:26 +0100 Subject: [PATCH 11/33] Binary basic columns should be limitable (#1253) --- CHANGELOG.md | 1 + .../sqlserver/schema_statements.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 68 ++++++++++--------- test/schema/sqlserver_specific_schema.rb | 1 + 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2e40f0b..ae573f62d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation - [#1247](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1247) Fix queries with date and date-time placeholder conditions +- [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable ## v7.2.1 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 29a172584..2fc52ea6f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -291,7 +291,7 @@ def check_constraints(table_name) end def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) - type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) + type_limitable = %w(string integer float char nchar varchar nvarchar binary_basic).include?(type.to_s) limit = nil unless type_limitable case type.to_s diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 405f84c93..6e7140ddf 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -93,39 +93,41 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :binary_col, type: "binary" # Our type methods. - _(columns["real_col"].sql_type).must_equal "real" - _(columns["money_col"].sql_type).must_equal "money" - _(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime" - _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)" - _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)" - _(columns["smallmoney_col"].sql_type).must_equal "smallmoney" - _(columns["char_col"].sql_type).must_equal "char(1)" - _(columns["varchar_col"].sql_type).must_equal "varchar(8000)" - _(columns["text_basic_col"].sql_type).must_equal "text" - _(columns["nchar_col"].sql_type).must_equal "nchar(1)" - _(columns["ntext_col"].sql_type).must_equal "ntext" - _(columns["binary_basic_col"].sql_type).must_equal "binary(1)" - _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)" - _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier" - _(columns["sstimestamp_col"].sql_type).must_equal "timestamp" - _(columns["json_col"].sql_type).must_equal "nvarchar(max)" - - assert_line :real_col, type: "real" - assert_line :money_col, type: "money", precision: 19, scale: 4 - assert_line :smalldatetime_col, type: "smalldatetime" - assert_line :datetime2_col, type: "datetime", precision: 7 - assert_line :datetimeoffset, type: "datetimeoffset", precision: 7 - assert_line :smallmoney_col, type: "smallmoney", precision: 10, scale: 4 - assert_line :char_col, type: "char", limit: 1 - assert_line :varchar_col, type: "varchar" - assert_line :text_basic_col, type: "text_basic" - assert_line :nchar_col, type: "nchar", limit: 1 - assert_line :ntext_col, type: "ntext" - assert_line :binary_basic_col, type: "binary_basic", limit: 1 - assert_line :varbinary_col, type: "varbinary" - assert_line :uuid_col, type: "uuid" - assert_line :sstimestamp_col, type: "ss_timestamp", null: false - assert_line :json_col, type: "text" + _(columns["real_col"].sql_type).must_equal "real" + _(columns["money_col"].sql_type).must_equal "money" + _(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime" + _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)" + _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)" + _(columns["smallmoney_col"].sql_type).must_equal "smallmoney" + _(columns["char_col"].sql_type).must_equal "char(1)" + _(columns["varchar_col"].sql_type).must_equal "varchar(8000)" + _(columns["text_basic_col"].sql_type).must_equal "text" + _(columns["nchar_col"].sql_type).must_equal "nchar(1)" + _(columns["ntext_col"].sql_type).must_equal "ntext" + _(columns["binary_basic_col"].sql_type).must_equal "binary(1)" + _(columns["binary_basic_16_col"].sql_type).must_equal "binary(16)" + _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)" + _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier" + _(columns["sstimestamp_col"].sql_type).must_equal "timestamp" + _(columns["json_col"].sql_type).must_equal "nvarchar(max)" + + assert_line :real_col, type: "real" + assert_line :money_col, type: "money", precision: 19, scale: 4 + assert_line :smalldatetime_col, type: "smalldatetime" + assert_line :datetime2_col, type: "datetime", precision: 7 + assert_line :datetimeoffset, type: "datetimeoffset", precision: 7 + assert_line :smallmoney_col, type: "smallmoney", precision: 10, scale: 4 + assert_line :char_col, type: "char", limit: 1 + assert_line :varchar_col, type: "varchar" + assert_line :text_basic_col, type: "text_basic" + assert_line :nchar_col, type: "nchar", limit: 1 + assert_line :ntext_col, type: "ntext" + assert_line :binary_basic_col, type: "binary_basic", limit: 1 + assert_line :binary_basic_16_col, type: "binary_basic", limit: 16 + assert_line :varbinary_col, type: "varbinary" + assert_line :uuid_col, type: "uuid" + assert_line :sstimestamp_col, type: "ss_timestamp", null: false + assert_line :json_col, type: "text" end it "dump column collation" do diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 136b6c1a5..a5160f791 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -33,6 +33,7 @@ t.nchar :nchar_col t.ntext :ntext_col t.binary_basic :binary_basic_col + t.binary_basic :binary_basic_16_col, limit: 16 t.varbinary :varbinary_col t.uuid :uuid_col t.ss_timestamp :sstimestamp_col From 47a6654461d7348ab0949c51d748faabec75f078 Mon Sep 17 00:00:00 2001 From: Steven Wallace <792372+cherez@users.noreply.github.com> Date: Fri, 8 Nov 2024 08:07:53 -0600 Subject: [PATCH 12/33] Fixed the ordering of optimizer hints in the generated SQL (#1255) --- CHANGELOG.md | 1 + lib/arel/visitors/sqlserver.rb | 14 ++++++-------- test/cases/optimizer_hints_test_sqlserver.rb | 9 +++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae573f62d..69ab388b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation - [#1247](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1247) Fix queries with date and date-time placeholder conditions - [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable +- [#1255](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1255)Fixed the ordering of optimizer hints in the generated SQL ## v7.2.1 diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index b4cef188d..5890b7fc3 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -134,26 +134,24 @@ def visit_Arel_Nodes_HomogeneousIn(o, collector) def visit_Arel_Nodes_SelectStatement(o, collector) @select_statement = o + optimizer_hints = nil distinct_One_As_One_Is_So_Not_Fetch o if o.with collector = visit o.with, collector collector << " " end - collector = o.cores.inject(collector) { |c, x| - visit_Arel_Nodes_SelectCore(x, c) - } + collector = o.cores.inject(collector) do |collect, core| + optimizer_hints = core.optimizer_hints if core.optimizer_hints + visit_Arel_Nodes_SelectCore(core, collect) + end collector = visit_Orders_And_Let_Fetch_Happen o, collector collector = visit_Make_Fetch_Happen o, collector + collector = maybe_visit optimizer_hints, collector collector ensure @select_statement = nil end - def visit_Arel_Nodes_SelectCore(o, collector) - collector = super - maybe_visit o.optimizer_hints, collector - end - def visit_Arel_Nodes_OptimizerHints(o, collector) hints = o.expr.map { |v| sanitize_as_option_clause(v) }.join(", ") collector << "OPTION (#{hints})" diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb index 97752f36c..46f13ac0d 100644 --- a/test/cases/optimizer_hints_test_sqlserver.rb +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -36,6 +36,15 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end end + + it "support order" do + assert_queries_match(%r{\ASELECT .+ FROM .+ ORDER .+ OPTION .+\z}) do + companies = Company.optimizer_hints("LABEL='FindCompanies'") + companies = companies.order(:id) + companies.to_a + end + end + it "sanitize values" do assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do companies = Company.optimizer_hints("OPTION (HASH GROUP)") From a74f49b268d3b3aafdf5d19c378cbf244ea9b371 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 8 Nov 2024 19:52:52 +0000 Subject: [PATCH 13/33] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69ab388b3..a5373f831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - [#1244](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1244) Allow INSERT statements with SELECT notation - [#1247](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1247) Fix queries with date and date-time placeholder conditions - [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable -- [#1255](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1255)Fixed the ordering of optimizer hints in the generated SQL +- [#1255](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1255) Fixed the ordering of optimizer hints in the generated SQL ## v7.2.1 From e06f1fd3d6d3fec690d9a82d921859ba114b4fa6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 10 Nov 2024 16:11:50 +0000 Subject: [PATCH 14/33] Release v7.2.2 --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5373f831..bb5900ec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.2 #### Fixed diff --git a/VERSION b/VERSION index b26a34e47..77f5bec5b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.1 +7.2.2 From f21c7f488f271a895e06879be833b5eabdf05ca6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 23 Nov 2024 20:43:34 +0000 Subject: [PATCH 15/33] Fix distinct alias when multiple databases used --- CHANGELOG.md | 6 ++++++ .../connection_adapters/sqlserver/schema_statements.rb | 10 +++++++--- test/cases/adapter_test_sqlserver.rb | 9 +++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb5900ec9..4f043bbf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1262](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1262) Fix distinct alias when multiple databases used. + ## v7.2.2 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 2fc52ea6f..3b07250e7 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -347,12 +347,16 @@ def add_timestamps(table_name, **options) def columns_for_distinct(columns, orders) order_columns = orders.reject(&:blank?).map { |s| - s = s.to_sql unless s.is_a?(String) + s = visitor.compile(s) unless s.is_a?(String) s.gsub(/\s+(?:ASC|DESC)\b/i, "") .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "") - }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } + } + .reject(&:blank?) + .reject { |s| columns.include?(s) } - (order_columns << super).join(", ") + order_columns_aliased = order_columns.map.with_index { |column, i| "#{column} AS alias_#{i}" } + + (order_columns_aliased << super).join(", ") end def update_table_definition(table_name, base) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 9921f23e6..7b9f0eca5 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -594,4 +594,13 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes assert_equal Task.where("starting < ?", DateTime.current).count, 1 end end + + describe "distinct select query" do + it "generated SQL does not contain unnecessary alias projection" do + sqls = capture_sql do + Post.includes(:comments).joins(:comments).first + end + assert_no_match(/AS alias_0/, sqls.first) + end + end end From 3c6cb72f104d41c54dce16396868efa6286cc6e2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 8 Dec 2024 12:16:52 +0000 Subject: [PATCH 16/33] Release v7.2.3 (#1264) --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f043bbf5..9788d62bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.3 #### Fixed diff --git a/VERSION b/VERSION index 77f5bec5b..429dc57af 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.2 +7.2.3 From b01b50b8d82cebf83a72c061956b92e746ad46bf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Dec 2024 11:16:30 +0000 Subject: [PATCH 17/33] Fix parsing of raw table name from SQL with extra parentheses (#1270) --- CHANGELOG.md | 6 ++++++ .../connection_adapters/sqlserver/schema_statements.rb | 2 +- test/cases/schema_test_sqlserver.rb | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9788d62bc..f98981be4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1270](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1270) Fix parsing of raw table name from SQL with extra parentheses + ## v7.2.3 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3b07250e7..1c986f41d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -690,7 +690,7 @@ def get_raw_table_name(sql) elsif s.match?(/^\s*UPDATE\s+.*/i) s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] else - s.match(/FROM\s+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] + s.match(/FROM[\s|\(]+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] end.strip end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index f9dbde8e6..124c2d6dc 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -101,5 +101,11 @@ class SchemaTestSQLServer < ActiveRecord::TestCase assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "INSERT INTO [with].[select notation] SELECT * FROM [table_name]") end end + + describe 'CREATE VIEW statements' do + it do + assert_equal "test_table_as", connection.send(:get_raw_table_name, "CREATE VIEW test_views ( test_table_a_id, test_table_b_id ) AS SELECT test_table_as.id as test_table_a_id, test_table_bs.id as test_table_b_id FROM (test_table_as with(nolock) LEFT JOIN test_table_bs with(nolock) ON (test_table_as.id = test_table_bs.test_table_a_id))") + end + end end end From f823782352e78206feb62028e922ae8651d8bddf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 20 Dec 2024 19:18:16 +0000 Subject: [PATCH 18/33] Not compatible with TinyTDS v3+ (#1277) --- CHANGELOG.md | 3 ++- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f98981be4..7503f02ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ #### Fixed - [#1270](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1270) Fix parsing of raw table name from SQL with extra parentheses - +- [#1277](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1277) Not compatible with TinyTDS v3+ +- ## v7.2.3 #### Fixed diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 47d6f0f13..1c8f8776a 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -28,5 +28,5 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "activerecord", "~> 7.2.0" - spec.add_dependency "tiny_tds" + spec.add_dependency "tiny_tds", "~> 2.0" end From 55e17fc540a838c2abb2bb753d14e7b66978ae07 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 21 Dec 2024 10:11:17 +0000 Subject: [PATCH 19/33] Revert "Not compatible with TinyTDS v3+ (#1277)" (#1280) This reverts commit f823782352e78206feb62028e922ae8651d8bddf. --- CHANGELOG.md | 3 +-- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7503f02ff..f98981be4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,7 @@ #### Fixed - [#1270](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1270) Fix parsing of raw table name from SQL with extra parentheses -- [#1277](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1277) Not compatible with TinyTDS v3+ -- + ## v7.2.3 #### Fixed diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 1c8f8776a..47d6f0f13 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -28,5 +28,5 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "activerecord", "~> 7.2.0" - spec.add_dependency "tiny_tds", "~> 2.0" + spec.add_dependency "tiny_tds" end From 4979857a2bd518f00d0b64b085b0fda51c193053 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 1 Jan 2025 20:47:16 +0000 Subject: [PATCH 20/33] Coerce test (#1283) --- test/cases/coerced_tests.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 21d419c9b..f3b16083c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -376,6 +376,18 @@ def test_payload_row_count_on_raw_sql_coerced end class CalculationsTest < ActiveRecord::TestCase + # SELECT columns must be in the GROUP clause. + coerce_tests! :test_should_count_with_group_by_qualified_name_on_loaded + def test_should_count_with_group_by_qualified_name_on_loaded_coerced + accounts = Account.group("accounts.id").select("accounts.id") + expected = { 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1 } + assert_not_predicate accounts, :loaded? + assert_equal expected, accounts.count + accounts.load + assert_predicate accounts, :loaded? + assert_equal expected, accounts.count(:id) + end + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_offset_is_kept def test_offset_is_kept_coerced From 527ba54c9d3cebb712b0c80a68e9c6aaca22890d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Jan 2025 13:20:06 +0000 Subject: [PATCH 21/33] Release v7.2.4 (#1287) --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f98981be4..fd61922ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.4 #### Fixed diff --git a/VERSION b/VERSION index 429dc57af..2bbaead44 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.3 +7.2.4 From faaa4c2ecd499eeb00c8bf4142c17f2dda74da8a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 10 Mar 2025 11:26:29 +0000 Subject: [PATCH 22/33] Fix retrieval of temporary table's column information (#1310) --- CHANGELOG.md | 6 +++++ .../sqlserver/schema_statements.rb | 26 +++++++++++++------ .../connection_adapters/sqlserver/utils.rb | 4 +++ test/cases/temporary_table_test_sqlserver.rb | 19 ++++++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 test/cases/temporary_table_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index fd61922ce..689028af2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1308](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1308) Fix retrieval of temporary table's column information. + ## v7.2.4 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 1c986f41d..828dcbff2 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -571,12 +571,22 @@ def column_definitions(table_name) end def column_definitions_sql(database, identifier) - object_name = prepared_statements ? "@0" : quote(identifier.object) - schema_name = if identifier.schema.blank? - "schema_name()" - else - prepared_statements ? "@1" : quote(identifier.schema) - end + schema_name = "schema_name()" + + if prepared_statements + object_name = "@0" + schema_name = "@1" if identifier.schema.present? + else + object_name = quote(identifier.object) + schema_name = quote(identifier.schema) if identifier.schema.present? + end + + object_id_arg = identifier.schema.present? ? "CONCAT(#{schema_name},'.',#{object_name})" : object_name + + if identifier.temporary_table? + database = "TEMPDB" + object_id_arg = "CONCAT('#{database}','..',#{object_name})" + end %{ SELECT @@ -631,7 +641,7 @@ def column_definitions_sql(database, identifier) AND k.unique_index_id = ic.index_id AND c.column_id = ic.column_id WHERE - o.name = #{object_name} + o.Object_ID = Object_ID(#{object_id_arg}) AND s.name = #{schema_name} ORDER BY c.column_id @@ -653,7 +663,7 @@ def remove_check_constraints(table_name, column_name) end def remove_default_constraint(table_name, column_name) - # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case. + # If there are foreign keys in this table, we could still get back a 2D array, so flatten just in case. execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row| row["constraint_type"] == "DEFAULT on column #{column_name}" end.each do |row| diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 002847919..5ebfeeb07 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -81,6 +81,10 @@ def hash parts.hash end + def temporary_table? + object.start_with?("#") + end + protected def parse_raw_name diff --git a/test/cases/temporary_table_test_sqlserver.rb b/test/cases/temporary_table_test_sqlserver.rb new file mode 100644 index 000000000..0ab808a70 --- /dev/null +++ b/test/cases/temporary_table_test_sqlserver.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class TemporaryTableSQLServer < ActiveRecord::TestCase + def test_insert_into_temporary_table + ActiveRecord::Base.with_connection do |conn| + conn.exec_query("CREATE TABLE #temp_users (id INT IDENTITY(1,1), name NVARCHAR(100))") + + result = conn.exec_query("SELECT * FROM #temp_users") + assert_equal 0, result.count + + conn.exec_query("INSERT INTO #temp_users (name) VALUES ('John'), ('Doe')") + + result = conn.exec_query("SELECT * FROM #temp_users") + assert_equal 2, result.count + end + end +end From c429a1c505610f71ae592ab22e47716275cab1a9 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 10 Mar 2025 11:36:30 +0000 Subject: [PATCH 23/33] Release v7.2.5 --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 689028af2..e16491181 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.5 #### Fixed diff --git a/VERSION b/VERSION index 2bbaead44..8aea167e7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.4 +7.2.5 From 838af65fab9a18ffb225da61bbfd4057a7405651 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 May 2025 10:36:57 +0100 Subject: [PATCH 24/33] Enable identity insert on view's base table for fixtures (#1333) --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 6 ++++++ .../sqlserver/database_statements.rb | 12 ++++++------ test/cases/view_test_sqlserver.rb | 12 +++++++++--- test/fixtures/sst_customers_view.yml | 6 ++++++ 5 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/sst_customers_view.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ec555c3b..984a6268d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: test: name: Run test suite - runs-on: ubuntu-20.04 # TODO: Change back to 'ubuntu-latest' when https://github.com/microsoft/mssql-docker/issues/899 resolved. + runs-on: ubuntu-latest env: COMPOSE_FILE: docker-compose.ci.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index e16491181..346eaf213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1333](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1333) Enable identity insert on view's base table for fixtures. + ## v7.2.5 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 64a3ed22b..d420ca6a2 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -42,9 +42,6 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa log(sql, name, binds, async: async) do |notification_payload| with_raw_connection do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) - # If the table name is a view, we need to get the base table name for enabling identity insert. - id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name) - with_identity_insert_enabled(id_insert_table_name, conn) do internal_exec_sql_query(sql, conn) end @@ -194,11 +191,14 @@ def execute_procedure(proc_name, *variables) end def with_identity_insert_enabled(table_name, conn) - table_name = quote_table_name(table_name) - set_identity_insert(table_name, conn, true) + # If the table name is a view, we need to get the base table name for enabling identity insert. + table_name = view_table_name(table_name) if view_exists?(table_name) + quoted_table_name = quote_table_name(table_name) + + set_identity_insert(quoted_table_name, conn, true) yield ensure - set_identity_insert(table_name, conn, false) + set_identity_insert(quoted_table_name, conn, false) end def use_database(database = nil) diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 84bfd80e1..03b3fef87 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -48,11 +48,17 @@ class ViewTestSQLServer < ActiveRecord::TestCase end end - describe 'identity insert' do - it "identity insert works with views" do - assert_difference("SSTestCustomersView.count", 1) do + describe "identity insert" do + it "creates table record through a view" do + assert_difference("SSTestCustomersView.count", 2) do SSTestCustomersView.create!(id: 5, name: "Bob") + SSTestCustomersView.create!(id: 6, name: "Tim") end end + + it "creates table records through a view using fixtures" do + ActiveRecord::FixtureSet.create_fixtures(File.join(ARTest::SQLServer.test_root_sqlserver, "fixtures"), ["sst_customers_view"]) + assert_equal SSTestCustomersView.all.count, 2 + end end end diff --git a/test/fixtures/sst_customers_view.yml b/test/fixtures/sst_customers_view.yml new file mode 100644 index 000000000..668ba3763 --- /dev/null +++ b/test/fixtures/sst_customers_view.yml @@ -0,0 +1,6 @@ +david: + name: "David" + balance: 2,004 +aidan: + name: "Aidan" + balance: 10,191 From b3db80f0f8a6fd85be62ed57d614b1da949fece2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 May 2025 11:18:56 +0100 Subject: [PATCH 25/33] Release v7.2.6 --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 346eaf213..f810c5573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.6 #### Fixed diff --git a/VERSION b/VERSION index 8aea167e7..ba6a7620d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.5 +7.2.6 From bea0578eaeba1c18d7f5b3a75dedb7084b00958c Mon Sep 17 00:00:00 2001 From: Adrian CB Date: Sat, 14 Jun 2025 00:55:59 +1000 Subject: [PATCH 26/33] Support more Azure services by changing language source (#1341) --- CHANGELOG.md | 6 ++++++ .../connection_adapters/sqlserver/database_statements.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f810c5573..44e244a01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Changed + +- [#1341](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1341) Support more Azure services by changing language source. + ## v7.2.6 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index d420ca6a2..b9f0f078f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -228,7 +228,7 @@ def user_options def user_options_dateformat if sqlserver_azure? - select_value "SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID", "SCHEMA" + select_value "SELECT [dateformat] FROM [sys].[syslanguages] WHERE [name] = @@LANGUAGE", "SCHEMA" else user_options["dateformat"] end From 8e75e393f6fbf47b47fe78d9a24855da1244c037 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 15 Aug 2025 14:52:48 +0100 Subject: [PATCH 27/33] Support cross database inserts (#1357) Co-authored-by: Chris Ortman --- .github/workflows/ci.yml | 1 + CHANGELOG.md | 4 +++ .../sqlserver/schema_statements.rb | 9 ++--- test/cases/adapter_test_sqlserver.rb | 35 +++++++++++++------ test/cases/temp_test_sqlserver.rb | 9 +++++ 5 files changed, 42 insertions(+), 16 deletions(-) create mode 100644 test/cases/temp_test_sqlserver.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 984a6268d..6ff557351 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ jobs: test: name: Run test suite runs-on: ubuntu-latest + timeout-minutes: 10 env: COMPOSE_FILE: docker-compose.ci.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e244a01..299be0ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [#1341](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1341) Support more Azure services by changing language source. +#### Fixed + +- [#1357(https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts. + ## v7.2.6 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 828dcbff2..6ccf31755 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -571,6 +571,7 @@ def column_definitions(table_name) end def column_definitions_sql(database, identifier) + database = "TEMPDB" if identifier.temporary_table? schema_name = "schema_name()" if prepared_statements @@ -581,12 +582,8 @@ def column_definitions_sql(database, identifier) schema_name = quote(identifier.schema) if identifier.schema.present? end - object_id_arg = identifier.schema.present? ? "CONCAT(#{schema_name},'.',#{object_name})" : object_name - - if identifier.temporary_table? - database = "TEMPDB" - object_id_arg = "CONCAT('#{database}','..',#{object_name})" - end + object_id_arg = identifier.schema.present? ? "CONCAT('.',#{schema_name},'.',#{object_name})" : "CONCAT('..',#{object_name})" + object_id_arg = "CONCAT('#{database}',#{object_id_arg})" %{ SELECT diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 7b9f0eca5..21b5fa340 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -7,10 +7,17 @@ require "models/subscriber" require "models/minimalistic" require "models/college" +require "models/dog" +require "models/other_dog" class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks + let(:arunit_connection) { Topic.lease_connection } + let(:arunit2_connection) { College.lease_connection } + let(:arunit_database) { arunit_connection.pool.db_config.database } + let(:arunit2_database) { arunit2_connection.pool.db_config.database } + let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" } @@ -50,8 +57,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists." # Test when database and owner included in table name. - db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") - Topic.table_name = "#{db_config.database}.dbo.topics" + Topic.table_name = "#{arunit_database}.dbo.topics" assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists." ensure Topic.table_name = "topics" @@ -59,12 +65,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "test table existence across database schemas" do - arunit_connection = Topic.lease_connection - arunit2_connection = College.lease_connection - - arunit_database = arunit_connection.pool.db_config.database - arunit2_database = arunit2_connection.pool.db_config.database - # Assert that connections use different default databases schemas. assert_not_equal arunit_database, arunit2_database @@ -200,6 +200,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_non_dbo_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([id],[name]) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" @identity_insert_sql_non_dbo_unquoted_sp = "EXEC sp_executesql N'INSERT INTO test.aliens (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" @identity_insert_sql_non_dbo_unordered_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Mork', @1 = 420" + + @non_identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs SELECT * FROM #{arunit_database}.dbo.dogs" + @identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs(id) SELECT id FROM #{arunit_database}.dbo.dogs" end it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do @@ -216,20 +219,32 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_sp) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted_sp) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered_sp) + + assert_equal "[#{arunit2_database}].[dbo].[dogs]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_cross_database) end it "return false to #query_requires_identity_insert? for normal SQL" do - [basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql| + [basic_insert_sql, basic_update_sql, basic_select_sql, @non_identity_insert_sql_cross_database].each do |sql| assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}" end end - it "find identity column using #identity_columns" do + it "find identity column" do task_id_column = Task.columns_hash["id"] assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type end + it "find identity column cross database" do + id_column = Dog.columns_hash["id"] + assert_equal id_column.name, arunit2_connection.send(:identity_columns, Dog.table_name).first.name + assert_equal id_column.sql_type, arunit2_connection.send(:identity_columns, Dog.table_name).first.sql_type + + id_column = OtherDog.columns_hash["id"] + assert_equal id_column.name, arunit_connection.send(:identity_columns, OtherDog.table_name).first.name + assert_equal id_column.sql_type, arunit_connection.send(:identity_columns, OtherDog.table_name).first.sql_type + end + it "return an empty array when calling #identity_columns for a table_name with no identity" do _(connection.send(:identity_columns, Subscriber.table_name)).must_equal [] end diff --git a/test/cases/temp_test_sqlserver.rb b/test/cases/temp_test_sqlserver.rb new file mode 100644 index 000000000..c9fae9490 --- /dev/null +++ b/test/cases/temp_test_sqlserver.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class TempTestSQLServer < ActiveRecord::TestCase + # it "assert true" do + # assert true + # end +end From 0b5865eeb9984e1a665443c71e7cb1a23be0d7d3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 15 Aug 2025 15:00:11 +0100 Subject: [PATCH 28/33] Fix typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 299be0ad4..140d4bfb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ #### Fixed -- [#1357(https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts. +- [#1357](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts. ## v7.2.6 From e44bf0d98c9885d30b528c894f1c5aa44831f127 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 19 Aug 2025 20:46:00 +0100 Subject: [PATCH 29/33] Release v7.2.7 --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 140d4bfb9..c5130321a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.7 #### Changed diff --git a/VERSION b/VERSION index ba6a7620d..4afc54e7b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.6 +7.2.7 From 70913282d86d4b507129649dd79cdf533de2e6eb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 2 Oct 2025 20:07:43 +0100 Subject: [PATCH 30/33] Verify connection before retrieving the database version (#1365) --- CHANGELOG.md | 6 ++++++ .../connection_adapters/sqlserver_adapter.rb | 12 ++++++------ test/cases/helper_sqlserver.rb | 8 ++++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5130321a..898c05922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1365](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1365) Verify connection before retrieving the database version. + ## v7.2.7 #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 3f3e72808..7ec025727 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -490,19 +490,19 @@ def initialize_dateformatter end def version_year - @version_year ||= begin - if sqlserver_version =~ /vNext/ + @version_year ||= + if /vNext/.match?(sqlserver_version) 2016 else /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i end - rescue StandardError - 2016 - end end def sqlserver_version - @sqlserver_version ||= _raw_select("SELECT @@version", @raw_connection).first.first.to_s + @sqlserver_version ||= begin + verify! + _raw_select("SELECT @@version", @raw_connection).first.first.to_s + end end private diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index c42e9c7c0..01edd824a 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -15,6 +15,14 @@ require "support/query_assertions" require "mocha/minitest" +Minitest.after_run do + puts "\n\n" + puts "=" * 80 + puts ActiveRecord::Base.lease_connection.send(:sqlserver_version) + puts "\nSQL Server Version Year: #{ActiveRecord::Base.lease_connection.get_database_version}" + puts "=" * 80 +end + module ActiveSupport class TestCase < ::Minitest::Test include ARTest::SQLServer::CoerceableTest From d1ea86a48867592be76550746f71b2bb8ff44e8b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 3 Oct 2025 13:54:17 +0100 Subject: [PATCH 31/33] Release v7.2.8 --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 898c05922..8fb533350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.8 #### Fixed diff --git a/VERSION b/VERSION index 4afc54e7b..31554632a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.7 +7.2.8 From 8b4e76e53dcf7df31d759f08d5dbe315077e18c9 Mon Sep 17 00:00:00 2001 From: Matt Taylor <70293171+mtaylor-vailsys@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:19:30 -0600 Subject: [PATCH 32/33] Fixed query logging so that filter parameters are respected (#1371) --- CHANGELOG.md | 6 + .../sqlserver/database_statements.rb | 11 +- test/cases/coerced_tests.rb | 140 +++++------------- test/cases/optimizer_hints_test_sqlserver.rb | 2 +- test/cases/showplan_test_sqlserver.rb | 4 +- test/cases/specific_schema_test_sqlserver.rb | 12 +- test/support/query_assertions.rb | 22 +++ 7 files changed, 77 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb533350..90a415cb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1371](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1371) Fixed query logging so that filter parameters are respected. + ## v7.2.8 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index b9f0f078f..c6f8bfd2e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -34,12 +34,13 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa check_if_write_query(sql) mark_transaction_written_if_write(sql) - unless without_prepared_statement?(binds) - types, params = sp_executesql_types_and_parameters(binds) - sql = sp_executesql_sql(sql, types, params, name) - end + type_casted_binds = type_casted_binds(binds) + log(sql, name, binds, type_casted_binds, async: async) do |notification_payload| + unless without_prepared_statement?(binds) + types, params = sp_executesql_types_and_parameters(binds) + sql = sp_executesql_sql(sql, types, params, name) + end - log(sql, name, binds, async: async) do |notification_payload| with_raw_connection do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) with_identity_insert_enabled(id_insert_table_name, conn) do diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f3b16083c..2025268f8 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -248,7 +248,7 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced def test_belongs_to_coerced client = Client.find(3) first_firm = companies(:first_firm) - assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do assert_equal first_firm, client.firm assert_equal first_firm.name, client.firm.name end @@ -257,21 +257,6 @@ def test_belongs_to_coerced module ActiveRecord class BindParameterTest < ActiveRecord::TestCase - # Same as original coerced test except log is found using `EXEC sp_executesql` wrapper. - coerce_tests! :test_binds_are_logged - def test_binds_are_logged_coerced - sub = Arel::Nodes::BindParam.new(1) - binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)] - sql = "select * from topics where id = #{sub.to_sql}" - - @connection.exec_query(sql, "SQL", binds) - - logged_sql = "EXEC sp_executesql N'#{sql}', N'#{sub.to_sql} int', #{sub.to_sql} = 1" - message = @subscriber.calls.find { |args| args[4][:sql] == logged_sql } - - assert_equal binds, message[4][:binds] - end - # SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`. coerce_tests! :test_statement_cache coerce_tests! :test_statement_cache_with_query_cache @@ -279,55 +264,6 @@ def test_binds_are_logged_coerced coerce_tests! :test_statement_cache_with_find_by coerce_tests! :test_statement_cache_with_in_clause coerce_tests! :test_statement_cache_with_sql_string_literal - - # Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper. - coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements - def test_bind_params_to_sql_with_prepared_statements_coerced - assert_bind_params_to_sql_coerced(prepared: true) - end - - def test_bind_params_to_sql_with_unprepared_statements_coerced - @connection.unprepared_statement do - assert_bind_params_to_sql_coerced(prepared: false) - end - end - - private - - def assert_bind_params_to_sql_coerced(prepared:) - table = Author.quoted_table_name - pk = "#{table}.#{Author.quoted_primary_key}" - - # prepared_statements: true - # - # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2) OR [authors].[id] IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3 - # - # prepared_statements: false - # - # SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL) - # - sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)" - sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3" - - authors = Author.where(id: [1, 2, 3, nil]) - assert_equal sql_unprepared, @connection.to_sql(authors.arel) - assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } - - # prepared_statements: true - # - # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3 - # - # prepared_statements: false - # - # SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3) - # - sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})" - sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3" - - authors = Author.where(id: [1, 2, 3, 9223372036854775808]) - assert_equal sql_unprepared, @connection.to_sql(authors.arel) - assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } - end end end @@ -387,7 +323,7 @@ def test_should_count_with_group_by_qualified_name_on_loaded_coerced assert_predicate accounts, :loaded? assert_equal expected, accounts.count(:id) end - + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_offset_is_kept def test_offset_is_kept_coerced @@ -508,7 +444,7 @@ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced def test_limit_is_kept_coerced queries = capture_sql { Account.limit(1).count } assert_equal 1, queries.length - assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first) + assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, queries.first) end # Match SQL Server limit implementation @@ -516,7 +452,7 @@ def test_limit_is_kept_coerced def test_limit_with_offset_is_kept_coerced queries = capture_sql { Account.limit(1).offset(1).count } assert_equal 1, queries.length - assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first) + assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/, queries.first) end # SQL Server needs an alias for the calculated column @@ -980,9 +916,9 @@ class FinderTest < ActiveRecord::TestCase # Assert SQL Server limit implementation coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced - assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries } - assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries } - assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries } + assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [3]) { Topic.take(3).entries } + assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [2]) { Topic.first(2).entries } + assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [5]) { Topic.last(5).entries } end # This fails only when run in the full test suite task. Just taking it out of the mix. @@ -1013,7 +949,7 @@ def test_condition_local_time_interpolation_with_default_timezone_utc_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_with_match def test_include_on_unloaded_relation_with_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal true, Customer.where(name: "David").include?(customers(:david)) end end @@ -1021,7 +957,7 @@ def test_include_on_unloaded_relation_with_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_without_match def test_include_on_unloaded_relation_without_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal false, Customer.where(name: "David").include?(customers(:mary)) end end @@ -1029,7 +965,7 @@ def test_include_on_unloaded_relation_without_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_with_match def test_member_on_unloaded_relation_with_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal true, Customer.where(name: "David").member?(customers(:david)) end end @@ -1037,7 +973,7 @@ def test_member_on_unloaded_relation_with_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_without_match def test_member_on_unloaded_relation_without_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal false, Customer.where(name: "David").member?(customers(:mary)) end end @@ -1052,7 +988,7 @@ def test_implicit_order_column_is_configurable_coerced assert_equal topics(:third), Topic.last c = Topic.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i) { Topic.last } ensure @@ -1066,7 +1002,7 @@ def test_implicit_order_set_to_primary_key_coerced Topic.implicit_order_column = "id" c = Topic.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i) { Topic.last } ensure @@ -1081,7 +1017,7 @@ def test_implicit_order_for_model_without_primary_key_coerced c = NonPrimaryKey.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i) { NonPrimaryKey.last } ensure @@ -1091,7 +1027,7 @@ def test_implicit_order_for_model_without_primary_key_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key def test_member_on_unloaded_relation_with_composite_primary_key_coerced - assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY/) do book = cpk_books(:cpk_great_author_first_book) assert Cpk::Book.where(title: "The first book").member?(book) end @@ -1106,7 +1042,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description")) - assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.first end ensure @@ -1120,7 +1056,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.last end end @@ -1132,7 +1068,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.first end end @@ -1145,7 +1081,7 @@ def test_implicit_order_column_reorders_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.first end ensure @@ -1155,7 +1091,7 @@ def test_implicit_order_column_reorders_query_constraints_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key def test_include_on_unloaded_relation_with_composite_primary_key_coerced - assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/) do book = cpk_books(:cpk_great_author_first_book) assert Cpk::Book.where(title: "The first book").include?(book) end @@ -1165,11 +1101,11 @@ def test_include_on_unloaded_relation_with_composite_primary_key_coerced coerce_tests! :test_nth_to_last_with_order_uses_limit def test_nth_to_last_with_order_uses_limit_coerced c = Topic.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do Topic.second_to_last end - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do Topic.order(:updated_at).second_to_last end end @@ -1217,7 +1153,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase def test_has_one_coerced firm = companies(:first_firm) first_account = Account.find(1) - assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do assert_equal first_account, firm.account assert_equal first_account.credit_limit, firm.account.credit_limit end @@ -1229,7 +1165,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase coerce_tests! :test_has_one_through_executes_limited_query def test_has_one_through_executes_limited_query_coerced boring_club = clubs(:boring_club) - assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do assert_equal boring_club, @member.general_club end end @@ -1436,7 +1372,7 @@ def test_having_with_binds_for_both_where_and_having # Find any limit via our expression. coerce_tests! %r{relations don't load all records in #inspect} def test_relations_dont_load_all_records_in_inspect_coerced - assert_queries_match(/NEXT @0 ROWS.*@0 = \d+/) do + assert_queries_match(/NEXT @0 ROWS/) do Post.all.inspect end end @@ -2124,7 +2060,7 @@ def test_merge_doesnt_duplicate_same_clauses_coerced non_mary_and_bob = Author.where.not(id: [mary, bob]) author_id = Author.lease_connection.quote_table_name("authors.id") - assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do + assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)/) do assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob) end @@ -2262,14 +2198,6 @@ class LogSubscriberTest < ActiveRecord::TestCase def test_verbose_query_logs_coerced original_test_verbose_query_logs end - - # Bindings logged slightly differently. - coerce_tests! :test_where_in_binds_logging_include_attribute_names - def test_where_in_binds_logging_include_attribute_names_coerced - Developer.where(id: [1, 2, 3, 4, 5]).load - wait - assert_match(%{@0 = 1, @1 = 2, @2 = 3, @3 = 4, @4 = 5 [["id", nil], ["id", nil], ["id", nil], ["id", nil], ["id", nil]]}, @logger.logged(:debug).last) - end end class ReloadModelsTest < ActiveRecord::TestCase @@ -2337,7 +2265,7 @@ def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attr c = Cpk::OrderAgreement.lease_connection order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id")) - order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/ + order_id_constraint = /#{order_id_column} = @0$/ expectation = /SELECT.*WHERE.* #{order_id_constraint}/ assert_match(expectation, preload_sql) @@ -2361,7 +2289,7 @@ def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_ c = Cpk::Order.lease_connection order_id = Regexp.escape(c.quote_table_name("cpk_orders.id")) - order_constraint = /#{order_id} = @0.*@0 = \d+$/ + order_constraint = /#{order_id} = @0$/ expectation = /SELECT.*WHERE.* #{order_constraint}/ assert_match(expectation, preload_sql) @@ -2432,7 +2360,7 @@ class QueryLogsTest < ActiveRecord::TestCase coerce_tests! :test_sql_commenter_format def test_sql_commenter_format_coerced ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) - assert_queries_match(%r{/\*application=''active_record''\*/}) do + assert_queries_match(%r{/\*application='active_record'\*/}) do Dashboard.first end end @@ -2447,7 +2375,7 @@ def test_sqlcommenter_format_value_coerced { tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } }, ] - assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do + assert_queries_match(%r{custom_proc='Joe%27s%20Shack',tracestate='congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7'\*/}) do Dashboard.first end end @@ -2462,7 +2390,7 @@ def test_sqlcommenter_format_value_string_coercible_coerced { custom_proc: -> { 1234 } }, ] - assert_queries_match(%r{custom_proc=''1234''\*/}) do + assert_queries_match(%r{custom_proc='1234'\*/}) do Dashboard.first end end @@ -2481,7 +2409,7 @@ def test_sqlcommenter_format_allows_string_keys_coerced }, ] - assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do + assert_queries_match(%r{custom_proc='Joe%27s%20Shack',string='value',tracestate='congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7'\*/}) do Dashboard.first end end @@ -2705,7 +2633,7 @@ class ExplainTest < ActiveRecord::TestCase def test_relation_explain_with_first_coerced expected_query = capture_sql { Car.all.first - }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1] + }.first[/(.*?) NEXT/, 1] message = Car.all.explain.first assert_match(/^EXPLAIN/, message) assert_match(expected_query, message) @@ -2716,7 +2644,7 @@ def test_relation_explain_with_first_coerced def test_relation_explain_with_last_coerced expected_query = capture_sql { Car.all.last - }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1] + }.first[/(.*?) NEXT/, 1] expected_query = expected_query message = Car.all.explain.last diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb index 46f13ac0d..274bc889b 100644 --- a/test/cases/optimizer_hints_test_sqlserver.rb +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -29,7 +29,7 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end it "support subqueries" do - assert_queries_match(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do + assert_queries_match(%r{SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)}) do companies = Company.optimizer_hints("MAXDOP 2") companies = companies.select(:id).where(firm_id: [0, 1]).limit(3) assert_equal 3, companies.count diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 3bf9e1538..5b01fb9d1 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -28,13 +28,13 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase it "from array condition using index" do plan = Car.where(id: [1, 2]).explain.inspect - _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" + _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]" _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end it "from array condition" do plan = Car.where(name: ["honda", "zyke"]).explain.inspect - _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" + _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]" _(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql" end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index bfdce3617..0829012e5 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -116,16 +116,16 @@ def quoted_id end end # Using ActiveRecord's quoted_id feature for objects. - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first } - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: value.new).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: value.new).first } # Using our custom char type data. type = ActiveRecord::Type::SQLServer::Char data = ActiveRecord::Type::SQLServer::Data - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first } - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first } # Taking care of everything. - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: "T").first } - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: "T").first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: "T").first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: "T").first } end it "can update and hence properly quoted non-national char/varchar columns" do diff --git a/test/support/query_assertions.rb b/test/support/query_assertions.rb index accb11f09..8d5a46221 100644 --- a/test/support/query_assertions.rb +++ b/test/support/query_assertions.rb @@ -22,6 +22,28 @@ def assert_queries_count(count = nil, include_schema: false, &block) end end + def assert_queries_and_values_match(match, bound_values=[], count: nil, &block) + ActiveRecord::Base.lease_connection.materialize_transactions + + counter = ActiveRecord::Assertions::QueryAssertions::SQLCounter.new + ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do + result = _assert_nothing_raised_or_warn("assert_queries_match", &block) + queries = counter.log_full + matched_queries = queries.select do |query, values| + values = values.map { |v| v.respond_to?(:quoted) ? v.quoted : v } + match === query && bound_values === values + end + + if count + assert_equal count, matched_queries.size, "#{matched_queries.size} instead of #{count} queries were executed.#{count.log.empty? ? '' : "\nQueries:\n#{counter.log.join("\n")}"}" + else + assert_operator matched_queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{counter.log.empty? ? '' : "\nQueries:\n#{counter.log.join("\n")}"}" + end + + result + end + end + private # Rails tests expect a save-point to be created and released. SQL Server does not release From 178d58d42637894d0628eeaf7d19ccfe0be1ba81 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 19:48:50 +0000 Subject: [PATCH 33/33] Release v7.2.9 (#1373) --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a415cb0..6d11ca954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.2.9 #### Fixed diff --git a/VERSION b/VERSION index 31554632a..672f66a61 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.8 +7.2.9