activerecord-sqlserver-adapter 7.1.7 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +3 -3
- data/.github/workflows/ci.yml +10 -4
- data/CHANGELOG.md +5 -99
- data/Gemfile +4 -4
- data/README.md +43 -19
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +2 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +7 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +14 -12
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +50 -32
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlserver/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +2 -5
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -32
- data/lib/arel/visitors/sqlserver.rb +57 -12
- data/test/cases/active_schema_test_sqlserver.rb +6 -6
- data/test/cases/adapter_test_sqlserver.rb +17 -18
- data/test/cases/coerced_tests.rb +279 -167
- data/test/cases/disconnected_test_sqlserver.rb +9 -3
- data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +1 -1
- data/test/cases/enum_test_sqlserver.rb +1 -1
- data/test/cases/execute_procedure_test_sqlserver.rb +9 -5
- data/test/cases/helper_sqlserver.rb +11 -5
- data/test/cases/index_test_sqlserver.rb +8 -6
- data/test/cases/json_test_sqlserver.rb +1 -1
- data/test/cases/lateral_test_sqlserver.rb +2 -2
- data/test/cases/migration_test_sqlserver.rb +1 -1
- data/test/cases/optimizer_hints_test_sqlserver.rb +12 -12
- data/test/cases/pessimistic_locking_test_sqlserver.rb +8 -7
- data/test/cases/primary_keys_test_sqlserver.rb +2 -2
- data/test/cases/rake_test_sqlserver.rb +8 -4
- data/test/cases/schema_dumper_test_sqlserver.rb +4 -5
- data/test/cases/showplan_test_sqlserver.rb +7 -7
- data/test/cases/specific_schema_test_sqlserver.rb +17 -13
- data/test/cases/view_test_sqlserver.rb +1 -1
- data/test/schema/sqlserver_specific_schema.rb +4 -4
- data/test/support/connection_reflection.rb +1 -1
- data/test/support/core_ext/query_cache.rb +2 -2
- data/test/support/query_assertions.rb +49 -0
- data/test/support/table_definition_sqlserver.rb +24 -0
- data/test/support/test_in_memory_oltp.rb +2 -2
- metadata +12 -13
- data/lib/active_record/sqlserver_base.rb +0 -13
- data/test/cases/scratchpad_test_sqlserver.rb +0 -8
- data/test/support/sql_counter_sqlserver.rb +0 -14
| @@ -4,9 +4,53 @@ module ActiveRecord | |
| 4 4 | 
             
              module ConnectionAdapters
         | 
| 5 5 | 
             
                module SQLServer
         | 
| 6 6 | 
             
                  module Quoting
         | 
| 7 | 
            +
                    extend ActiveSupport::Concern
         | 
| 8 | 
            +
             | 
| 7 9 | 
             
                    QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
         | 
| 8 10 | 
             
                    QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
         | 
| 9 11 |  | 
| 12 | 
            +
                    module ClassMethods
         | 
| 13 | 
            +
                      def column_name_matcher
         | 
| 14 | 
            +
                        /
         | 
| 15 | 
            +
                          \A
         | 
| 16 | 
            +
                          (
         | 
| 17 | 
            +
                            (?:
         | 
| 18 | 
            +
                              # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument)
         | 
| 19 | 
            +
                              ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\))
         | 
| 20 | 
            +
                            )
         | 
| 21 | 
            +
                            (?:\s+AS\s+(?:\w+|\[\w+\]))?
         | 
| 22 | 
            +
                          )
         | 
| 23 | 
            +
                          (?:\s*,\s*\g<1>)*
         | 
| 24 | 
            +
                          \z
         | 
| 25 | 
            +
                        /ix
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      def column_name_with_order_matcher
         | 
| 29 | 
            +
                        /
         | 
| 30 | 
            +
                          \A
         | 
| 31 | 
            +
                          (
         | 
| 32 | 
            +
                            (?:
         | 
| 33 | 
            +
                              # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument)
         | 
| 34 | 
            +
                              ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\))
         | 
| 35 | 
            +
                            )
         | 
| 36 | 
            +
                            (?:\s+COLLATE\s+\w+)?
         | 
| 37 | 
            +
                            (?:\s+ASC|\s+DESC)?
         | 
| 38 | 
            +
                            (?:\s+NULLS\s+(?:FIRST|LAST))?
         | 
| 39 | 
            +
                          )
         | 
| 40 | 
            +
                          (?:\s*,\s*\g<1>)*
         | 
| 41 | 
            +
                          \z
         | 
| 42 | 
            +
                        /ix
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      def quote_column_name(name)
         | 
| 46 | 
            +
                        QUOTED_COLUMN_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                      def quote_table_name(name)
         | 
| 50 | 
            +
                        QUOTED_TABLE_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted
         | 
| 51 | 
            +
                      end
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 10 54 | 
             
                    def fetch_type_metadata(sql_type, sqlserver_options = {})
         | 
| 11 55 | 
             
                      cast_type = lookup_cast_type(sql_type)
         | 
| 12 56 |  | 
| @@ -33,14 +77,6 @@ module ActiveRecord | |
| 33 77 | 
             
                      SQLServer::Utils.quote_string_single_national(s)
         | 
| 34 78 | 
             
                    end
         | 
| 35 79 |  | 
| 36 | 
            -
                    def quote_column_name(name)
         | 
| 37 | 
            -
                      QUOTED_COLUMN_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted
         | 
| 38 | 
            -
                    end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                    def quote_table_name(name)
         | 
| 41 | 
            -
                      QUOTED_TABLE_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted
         | 
| 42 | 
            -
                    end
         | 
| 43 | 
            -
             | 
| 44 80 | 
             
                    def quote_default_expression(value, column)
         | 
| 45 81 | 
             
                      cast_type = lookup_cast_type(column.sql_type)
         | 
| 46 82 | 
             
                      if cast_type.type == :uuid && value.is_a?(String) && value.include?('()')
         | 
| @@ -76,44 +112,6 @@ module ActiveRecord | |
| 76 112 | 
             
                      end
         | 
| 77 113 | 
             
                    end
         | 
| 78 114 |  | 
| 79 | 
            -
                    def column_name_matcher
         | 
| 80 | 
            -
                      COLUMN_NAME
         | 
| 81 | 
            -
                    end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                    def column_name_with_order_matcher
         | 
| 84 | 
            -
                      COLUMN_NAME_WITH_ORDER
         | 
| 85 | 
            -
                    end
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                    COLUMN_NAME = /
         | 
| 88 | 
            -
                      \A
         | 
| 89 | 
            -
                      (
         | 
| 90 | 
            -
                        (?:
         | 
| 91 | 
            -
                          # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument)
         | 
| 92 | 
            -
                          ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\))
         | 
| 93 | 
            -
                        )
         | 
| 94 | 
            -
                        (?:\s+AS\s+(?:\w+|\[\w+\]))?
         | 
| 95 | 
            -
                      )
         | 
| 96 | 
            -
                      (?:\s*,\s*\g<1>)*
         | 
| 97 | 
            -
                      \z
         | 
| 98 | 
            -
                    /ix
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                    COLUMN_NAME_WITH_ORDER = /
         | 
| 101 | 
            -
                      \A
         | 
| 102 | 
            -
                      (
         | 
| 103 | 
            -
                        (?:
         | 
| 104 | 
            -
                          # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument)
         | 
| 105 | 
            -
                          ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\))
         | 
| 106 | 
            -
                        )
         | 
| 107 | 
            -
                        (?:\s+COLLATE\s+\w+)?
         | 
| 108 | 
            -
                        (?:\s+ASC|\s+DESC)?
         | 
| 109 | 
            -
                        (?:\s+NULLS\s+(?:FIRST|LAST))?
         | 
| 110 | 
            -
                      )
         | 
| 111 | 
            -
                      (?:\s*,\s*\g<1>)*
         | 
| 112 | 
            -
                      \z
         | 
| 113 | 
            -
                    /ix
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                    private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
         | 
| 116 | 
            -
             | 
| 117 115 | 
             
                    def quote(value)
         | 
| 118 116 | 
             
                      case value
         | 
| 119 117 | 
             
                      when Type::Binary::Data
         | 
| @@ -16,6 +16,8 @@ module ActiveRecord | |
| 16 16 | 
             
                      internal_execute("ROLLBACK TRANSACTION #{name}", "TRANSACTION")
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 | 
            +
                    # SQL Server does require save-points to be explicitly released.
         | 
| 20 | 
            +
                    # See https://stackoverflow.com/questions/3101312/sql-server-2008-no-release-savepoint-for-current-transaction
         | 
| 19 21 | 
             
                    def release_savepoint(_name)
         | 
| 20 22 | 
             
                    end
         | 
| 21 23 | 
             
                  end
         | 
| @@ -132,7 +132,7 @@ module ActiveRecord | |
| 132 132 | 
             
                      schema_cache.clear_data_source_cache!(table_name.to_s)
         | 
| 133 133 | 
             
                      schema_cache.clear_data_source_cache!(new_name.to_s)
         | 
| 134 134 | 
             
                      execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
         | 
| 135 | 
            -
                      rename_table_indexes(table_name, new_name)
         | 
| 135 | 
            +
                      rename_table_indexes(table_name, new_name, **options)
         | 
| 136 136 | 
             
                    end
         | 
| 137 137 |  | 
| 138 138 | 
             
                    def remove_column(table_name, column_name, type = nil, **options)
         | 
| @@ -29,12 +29,13 @@ require "active_record/connection_adapters/sqlserver/showplan" | |
| 29 29 | 
             
            require "active_record/connection_adapters/sqlserver/table_definition"
         | 
| 30 30 | 
             
            require "active_record/connection_adapters/sqlserver/quoting"
         | 
| 31 31 | 
             
            require "active_record/connection_adapters/sqlserver/utils"
         | 
| 32 | 
            -
            require "active_record/sqlserver_base"
         | 
| 33 32 | 
             
            require "active_record/connection_adapters/sqlserver_column"
         | 
| 34 33 | 
             
            require "active_record/tasks/sqlserver_database_tasks"
         | 
| 35 34 |  | 
| 36 35 | 
             
            module ActiveRecord
         | 
| 37 36 | 
             
              module ConnectionAdapters
         | 
| 37 | 
            +
                register "sqlserver", "ActiveRecord::ConnectionAdapters::SQLServerAdapter", "active_record/connection_adapters/sqlserver_adapter"
         | 
| 38 | 
            +
             | 
| 38 39 | 
             
                class SQLServerAdapter < AbstractAdapter
         | 
| 39 40 | 
             
                  include SQLServer::Version,
         | 
| 40 41 | 
             
                          SQLServer::Quoting,
         | 
| @@ -324,10 +325,6 @@ module ActiveRecord | |
| 324 325 | 
             
                    self.class::VERSION
         | 
| 325 326 | 
             
                  end
         | 
| 326 327 |  | 
| 327 | 
            -
                  def inspect
         | 
| 328 | 
            -
                    "#<#{self.class} version: #{version}, azure: #{sqlserver_azure?.inspect}>"
         | 
| 329 | 
            -
                  end
         | 
| 330 | 
            -
             | 
| 331 328 | 
             
                  def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil)
         | 
| 332 329 | 
             
                    result = from_clause + join_clause + where_clause + having_clause
         | 
| 333 330 | 
             
                    result << offset if offset
         | 
| @@ -10,7 +10,7 @@ module ActiveRecord | |
| 10 10 | 
             
                class SQLServerDatabaseTasks
         | 
| 11 11 | 
             
                  DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS"
         | 
| 12 12 |  | 
| 13 | 
            -
                  delegate : | 
| 13 | 
            +
                  delegate :with_connection, :establish_connection, to: ActiveRecord::Base
         | 
| 14 14 |  | 
| 15 15 | 
             
                  def self.using_database_configurations?
         | 
| 16 16 | 
             
                    true
         | 
| @@ -23,8 +23,10 @@ module ActiveRecord | |
| 23 23 |  | 
| 24 24 | 
             
                  def create(master_established = false)
         | 
| 25 25 | 
             
                    establish_master_connection unless master_established
         | 
| 26 | 
            -
                     | 
| 27 | 
            -
             | 
| 26 | 
            +
                    with_connection do |connection|
         | 
| 27 | 
            +
                      connection.create_database(configuration.database, configuration_hash.merge(collation: default_collation))
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                    establish_connection(configuration)
         | 
| 28 30 | 
             
                  rescue ActiveRecord::StatementInvalid => e
         | 
| 29 31 | 
             
                    if /database .* already exists/i === e.message
         | 
| 30 32 | 
             
                      raise DatabaseAlreadyExists
         | 
| @@ -35,15 +37,15 @@ module ActiveRecord | |
| 35 37 |  | 
| 36 38 | 
             
                  def drop
         | 
| 37 39 | 
             
                    establish_master_connection
         | 
| 38 | 
            -
                    connection.drop_database | 
| 40 | 
            +
                    with_connection { |connection| connection.drop_database(configuration.database) }
         | 
| 39 41 | 
             
                  end
         | 
| 40 42 |  | 
| 41 43 | 
             
                  def charset
         | 
| 42 | 
            -
                    connection.charset
         | 
| 44 | 
            +
                    with_connection { |connection| connection.charset }
         | 
| 43 45 | 
             
                  end
         | 
| 44 46 |  | 
| 45 47 | 
             
                  def collation
         | 
| 46 | 
            -
                    connection.collation
         | 
| 48 | 
            +
                    with_connection { |connection| connection.collation }
         | 
| 47 49 | 
             
                  end
         | 
| 48 50 |  | 
| 49 51 | 
             
                  def purge
         | 
| @@ -56,34 +58,38 @@ module ActiveRecord | |
| 56 58 | 
             
                    ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
         | 
| 57 59 | 
             
                  end
         | 
| 58 60 |  | 
| 59 | 
            -
                  def structure_dump(filename,  | 
| 60 | 
            -
                     | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
                       | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 61 | 
            +
                  def structure_dump(filename, _extra_flags)
         | 
| 62 | 
            +
                    with_connection do |connection|
         | 
| 63 | 
            +
                      server_arg = "-S #{Shellwords.escape(configuration_hash[:host])}"
         | 
| 64 | 
            +
                      server_arg += ":#{Shellwords.escape(configuration_hash[:port])}" if configuration_hash[:port]
         | 
| 65 | 
            +
                      command = [
         | 
| 66 | 
            +
                        "defncopy-ttds",
         | 
| 67 | 
            +
                        server_arg,
         | 
| 68 | 
            +
                        "-D #{Shellwords.escape(configuration_hash[:database])}",
         | 
| 69 | 
            +
                        "-U #{Shellwords.escape(configuration_hash[:username])}",
         | 
| 70 | 
            +
                        "-P #{Shellwords.escape(configuration_hash[:password])}",
         | 
| 71 | 
            +
                        "-o #{Shellwords.escape(filename)}",
         | 
| 72 | 
            +
                      ]
         | 
| 73 | 
            +
                      table_args = connection.tables.map { |t| Shellwords.escape(t) }
         | 
| 74 | 
            +
                      command.concat(table_args)
         | 
| 75 | 
            +
                      view_args = connection.views.map { |v| Shellwords.escape(v) }
         | 
| 76 | 
            +
                      command.concat(view_args)
         | 
| 77 | 
            +
                      raise "Error dumping database" unless Kernel.system(command.join(" "))
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                      dump = File.read(filename)
         | 
| 80 | 
            +
                      dump.gsub!(/^USE .*$\nGO\n/, "")                      # Strip db USE statements
         | 
| 81 | 
            +
                      dump.gsub!(/^GO\n/, "")                               # Strip db GO statements
         | 
| 82 | 
            +
                      dump.gsub!(/nvarchar\(8000\)/, "nvarchar(4000)")      # Fix nvarchar(8000) column defs
         | 
| 83 | 
            +
                      dump.gsub!(/nvarchar\(-1\)/, "nvarchar(max)")         # Fix nvarchar(-1) column defs
         | 
| 84 | 
            +
                      dump.gsub!(/text\(\d+\)/, "text")                     # Fix text(16) column defs
         | 
| 85 | 
            +
                      File.open(filename, "w") { |file| file.puts dump }
         | 
| 86 | 
            +
                    end
         | 
| 83 87 | 
             
                  end
         | 
| 84 88 |  | 
| 85 | 
            -
                  def structure_load(filename,  | 
| 86 | 
            -
                    connection | 
| 89 | 
            +
                  def structure_load(filename, _extra_flags)
         | 
| 90 | 
            +
                    with_connection do |connection|
         | 
| 91 | 
            +
                      connection.execute File.read(filename)
         | 
| 92 | 
            +
                    end
         | 
| 87 93 | 
             
                  end
         | 
| 88 94 |  | 
| 89 95 | 
             
                  private
         | 
| @@ -30,10 +30,46 @@ module Arel | |
| 30 30 | 
             
                  end
         | 
| 31 31 |  | 
| 32 32 | 
             
                  def visit_Arel_Nodes_UpdateStatement(o, collector)
         | 
| 33 | 
            -
                    if  | 
| 34 | 
            -
                      o | 
| 33 | 
            +
                    if has_join_and_composite_primary_key?(o)
         | 
| 34 | 
            +
                      update_statement_using_join(o, collector)
         | 
| 35 | 
            +
                    else
         | 
| 36 | 
            +
                      o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil?
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      super
         | 
| 35 39 | 
             
                    end
         | 
| 36 | 
            -
             | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def visit_Arel_Nodes_DeleteStatement(o, collector)
         | 
| 43 | 
            +
                    if has_join_and_composite_primary_key?(o)
         | 
| 44 | 
            +
                      delete_statement_using_join(o, collector)
         | 
| 45 | 
            +
                    else
         | 
| 46 | 
            +
                      super
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def has_join_and_composite_primary_key?(o)
         | 
| 51 | 
            +
                    has_join_sources?(o) && o.relation.left.instance_variable_get(:@klass).composite_primary_key?
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def delete_statement_using_join(o, collector)
         | 
| 55 | 
            +
                    collector.retryable = false
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    collector << "DELETE "
         | 
| 58 | 
            +
                    visit o.relation.left, collector
         | 
| 59 | 
            +
                    collector << " FROM "
         | 
| 60 | 
            +
                    visit o.relation, collector
         | 
| 61 | 
            +
                    collect_nodes_for o.wheres, collector, " WHERE ", " AND "
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def update_statement_using_join(o, collector)
         | 
| 65 | 
            +
                    collector.retryable = false
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    collector << "UPDATE "
         | 
| 68 | 
            +
                    visit o.relation.left, collector
         | 
| 69 | 
            +
                    collect_nodes_for o.values, collector, " SET "
         | 
| 70 | 
            +
                    collector << " FROM "
         | 
| 71 | 
            +
                    visit o.relation, collector
         | 
| 72 | 
            +
                    collect_nodes_for o.wheres, collector, " WHERE ", " AND "
         | 
| 37 73 | 
             
                  end
         | 
| 38 74 |  | 
| 39 75 | 
             
                  def visit_Arel_Nodes_Lock(o, collector)
         | 
| @@ -129,10 +165,12 @@ module Arel | |
| 129 165 | 
             
                    # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450
         | 
| 130 166 | 
             
                    table_name =
         | 
| 131 167 | 
             
                      begin
         | 
| 132 | 
            -
                         | 
| 133 | 
            -
                           | 
| 134 | 
            -
             | 
| 135 | 
            -
                           | 
| 168 | 
            +
                        o.class.engine.with_connection do |connection|
         | 
| 169 | 
            +
                          if connection.respond_to?(:sqlserver?) && connection.database_prefix_remote_server?
         | 
| 170 | 
            +
                            remote_server_table_name(o)
         | 
| 171 | 
            +
                          else
         | 
| 172 | 
            +
                            quote_table_name(o.name)
         | 
| 173 | 
            +
                          end
         | 
| 136 174 | 
             
                        end
         | 
| 137 175 | 
             
                      rescue Exception
         | 
| 138 176 | 
             
                        quote_table_name(o.name)
         | 
| @@ -201,6 +239,11 @@ module Arel | |
| 201 239 | 
             
                    collector
         | 
| 202 240 | 
             
                  end
         | 
| 203 241 |  | 
| 242 | 
            +
                  def visit_Arel_Nodes_WithRecursive(o, collector)
         | 
| 243 | 
            +
                    collector << "WITH "
         | 
| 244 | 
            +
                    collect_ctes(o.children, collector)
         | 
| 245 | 
            +
                  end
         | 
| 246 | 
            +
             | 
| 204 247 | 
             
                  # SQLServer ToSql/Visitor (Additions)
         | 
| 205 248 |  | 
| 206 249 | 
             
                  def visit_Arel_Nodes_SelectStatement_SQLServer_Lock(collector, options = {})
         | 
| @@ -315,14 +358,16 @@ module Arel | |
| 315 358 | 
             
                  end
         | 
| 316 359 |  | 
| 317 360 | 
             
                  def remote_server_table_name(o)
         | 
| 318 | 
            -
                     | 
| 319 | 
            -
                       | 
| 320 | 
            -
             | 
| 361 | 
            +
                    o.class.engine.with_connection do |connection|
         | 
| 362 | 
            +
                      ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers(
         | 
| 363 | 
            +
                        "#{connection.database_prefix}#{o.name}"
         | 
| 364 | 
            +
                      ).quoted
         | 
| 365 | 
            +
                    end
         | 
| 321 366 | 
             
                  end
         | 
| 322 367 |  | 
| 323 | 
            -
                  # Need to remove ordering from  | 
| 368 | 
            +
                  # Need to remove ordering from sub-queries unless TOP/OFFSET also used. Otherwise, SQLServer
         | 
| 324 369 | 
             
                  # returns error "The ORDER BY clause is invalid in views, inline functions, derived tables,
         | 
| 325 | 
            -
                  #  | 
| 370 | 
            +
                  # sub-queries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified."
         | 
| 326 371 | 
             
                  def remove_invalid_ordering_from_select_statement(node)
         | 
| 327 372 | 
             
                    return unless Arel::Nodes::SelectStatement === node
         | 
| 328 373 |  | 
| @@ -16,38 +16,38 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase | |
| 16 16 | 
             
                end
         | 
| 17 17 |  | 
| 18 18 | 
             
                it 'default index' do
         | 
| 19 | 
            -
                   | 
| 19 | 
            +
                  assert_queries_match('CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
         | 
| 20 20 | 
             
                    connection.add_index :schema_test_table, "foo"
         | 
| 21 21 | 
             
                  end
         | 
| 22 22 | 
             
                end
         | 
| 23 23 |  | 
| 24 24 | 
             
                it 'unique index' do
         | 
| 25 | 
            -
                   | 
| 25 | 
            +
                  assert_queries_match('CREATE UNIQUE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
         | 
| 26 26 | 
             
                    connection.add_index :schema_test_table, "foo", unique: true
         | 
| 27 27 | 
             
                  end
         | 
| 28 28 | 
             
                end
         | 
| 29 29 |  | 
| 30 30 | 
             
                it 'where condition on index' do
         | 
| 31 | 
            -
                   | 
| 31 | 
            +
                  assert_queries_match("CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo]) WHERE state = 'active'") do
         | 
| 32 32 | 
             
                    connection.add_index :schema_test_table, "foo", where: "state = 'active'"
         | 
| 33 33 | 
             
                  end
         | 
| 34 34 | 
             
                end
         | 
| 35 35 |  | 
| 36 36 | 
             
                it 'if index does not exist' do
         | 
| 37 | 
            -
                   | 
| 37 | 
            +
                  assert_queries_match("IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = 'index_schema_test_table_on_foo') " \
         | 
| 38 38 | 
             
                             "CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do
         | 
| 39 39 | 
             
                    connection.add_index :schema_test_table, "foo", if_not_exists: true
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 | 
             
                end
         | 
| 42 42 |  | 
| 43 43 | 
             
                it 'clustered index' do
         | 
| 44 | 
            -
                   | 
| 44 | 
            +
                  assert_queries_match('CREATE CLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
         | 
| 45 45 | 
             
                    connection.add_index :schema_test_table, "foo", type: :clustered
         | 
| 46 46 | 
             
                  end
         | 
| 47 47 | 
             
                end
         | 
| 48 48 |  | 
| 49 49 | 
             
                it 'nonclustered index' do
         | 
| 50 | 
            -
                   | 
| 50 | 
            +
                  assert_queries_match('CREATE NONCLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
         | 
| 51 51 | 
             
                    connection.add_index :schema_test_table, "foo", type: :nonclustered
         | 
| 52 52 | 
             
                  end
         | 
| 53 53 | 
             
                end
         | 
| @@ -18,8 +18,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 18 18 | 
             
              it "has basic and non-sensitive information in the adapters inspect method" do
         | 
| 19 19 | 
             
                string = connection.inspect
         | 
| 20 20 | 
             
                _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter}
         | 
| 21 | 
            -
                _(string).must_match %r{version\: \d.\d}
         | 
| 22 | 
            -
                _(string).must_match %r{azure: (true|false)}
         | 
| 23 21 | 
             
                _(string).wont_match %r{host}
         | 
| 24 22 | 
             
                _(string).wont_match %r{password}
         | 
| 25 23 | 
             
                _(string).wont_match %r{username}
         | 
| @@ -31,7 +29,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 31 29 | 
             
              end
         | 
| 32 30 |  | 
| 33 31 | 
             
              it "raises invalid statement error for bad SQL" do
         | 
| 34 | 
            -
                assert_raise(ActiveRecord::StatementInvalid) { Topic. | 
| 32 | 
            +
                assert_raise(ActiveRecord::StatementInvalid) { Topic.lease_connection.update("UPDATE XXX") }
         | 
| 35 33 | 
             
              end
         | 
| 36 34 |  | 
| 37 35 | 
             
              it "is has our adapter_name" do
         | 
| @@ -61,8 +59,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 61 59 | 
             
              end
         | 
| 62 60 |  | 
| 63 61 | 
             
              it "test table existence across database schemas" do
         | 
| 64 | 
            -
                arunit_connection = Topic. | 
| 65 | 
            -
                arunit2_connection = College. | 
| 62 | 
            +
                arunit_connection = Topic.lease_connection
         | 
| 63 | 
            +
                arunit2_connection = College.lease_connection
         | 
| 66 64 |  | 
| 67 65 | 
             
                arunit_database = arunit_connection.pool.db_config.database
         | 
| 68 66 | 
             
                arunit2_database = arunit2_connection.pool.db_config.database
         | 
| @@ -102,8 +100,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 102 100 | 
             
                assert_raise ActiveRecord::NoDatabaseError do
         | 
| 103 101 | 
             
                  db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
         | 
| 104 102 | 
             
                  configuration = db_config.configuration_hash.merge(database: "nonexistent_activerecord_unittest")
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                  connection = ActiveRecord::Base.sqlserver_connection configuration
         | 
| 103 | 
            +
                  connection = ActiveRecord::ConnectionAdapters::SQLServerAdapter.new(configuration)
         | 
| 107 104 | 
             
                  connection.exec_query("SELECT 1")
         | 
| 108 105 | 
             
                end
         | 
| 109 106 | 
             
              end
         | 
| @@ -285,23 +282,25 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 285 282 | 
             
                end
         | 
| 286 283 |  | 
| 287 284 | 
             
                it "NOT ALLOW by default the deletion of a referenced parent" do
         | 
| 288 | 
            -
                  SSTestHasPk. | 
| 285 | 
            +
                  SSTestHasPk.lease_connection.disable_referential_integrity {}
         | 
| 289 286 | 
             
                  assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy }
         | 
| 290 287 | 
             
                end
         | 
| 291 288 |  | 
| 292 289 | 
             
                it "ALLOW deletion of referenced parent using #disable_referential_integrity block" do
         | 
| 293 | 
            -
                  SSTestHasPk. | 
| 290 | 
            +
                  assert_difference("SSTestHasPk.count", -1) do
         | 
| 291 | 
            +
                    SSTestHasPk.lease_connection.disable_referential_integrity { @parent.destroy }
         | 
| 292 | 
            +
                  end
         | 
| 294 293 | 
             
                end
         | 
| 295 294 |  | 
| 296 295 | 
             
                it "again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block" do
         | 
| 297 296 | 
             
                  assert_raise(ActiveRecord::StatementInvalid) do
         | 
| 298 | 
            -
                    SSTestHasPk. | 
| 297 | 
            +
                    SSTestHasPk.lease_connection.disable_referential_integrity {}
         | 
| 299 298 | 
             
                    @parent.destroy
         | 
| 300 299 | 
             
                  end
         | 
| 301 300 | 
             
                end
         | 
| 302 301 |  | 
| 303 302 | 
             
                it "not disable referential integrity for the same table twice" do
         | 
| 304 | 
            -
                  tables = SSTestHasPk. | 
| 303 | 
            +
                  tables = SSTestHasPk.lease_connection.tables_with_referential_integrity
         | 
| 305 304 | 
             
                  assert_equal tables.size, tables.uniq.size
         | 
| 306 305 | 
             
                end
         | 
| 307 306 | 
             
              end
         | 
| @@ -396,7 +395,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 396 395 |  | 
| 397 396 | 
             
                it "allows connection#view_information to work across databases when using qualified object names" do
         | 
| 398 397 | 
             
                  # College is defined in activerecord_unittest2 database.
         | 
| 399 | 
            -
                  view_info = College. | 
| 398 | 
            +
                  view_info = College.lease_connection.send(:view_information, "[activerecord_unittest].[dbo].[sst_customers_view]")
         | 
| 400 399 | 
             
                  assert_equal("sst_customers_view", view_info["TABLE_NAME"])
         | 
| 401 400 | 
             
                  assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"])
         | 
| 402 401 | 
             
                end
         | 
| @@ -407,8 +406,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 407 406 | 
             
                end
         | 
| 408 407 |  | 
| 409 408 | 
             
                it "allow the connection#view_table_name method to return true table_name for the view for other connections" do
         | 
| 410 | 
            -
                  assert_equal "customers", College. | 
| 411 | 
            -
                  assert_equal "topics", College. | 
| 409 | 
            +
                  assert_equal "customers", College.lease_connection.send(:view_table_name, "[activerecord_unittest].[dbo].[sst_customers_view]")
         | 
| 410 | 
            +
                  assert_equal "topics", College.lease_connection.send(:view_table_name, "topics"), "No view here, the same table name should come back."
         | 
| 412 411 | 
             
                end
         | 
| 413 412 | 
             
                # With same column names
         | 
| 414 413 |  | 
| @@ -434,7 +433,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 434 433 | 
             
                end
         | 
| 435 434 |  | 
| 436 435 | 
             
                it "respond true to data_source_exists?" do
         | 
| 437 | 
            -
                  assert SSTestCustomersView. | 
| 436 | 
            +
                  assert SSTestCustomersView.lease_connection.data_source_exists?(SSTestCustomersView.table_name)
         | 
| 438 437 | 
             
                end
         | 
| 439 438 |  | 
| 440 439 | 
             
                # With aliased column names
         | 
| @@ -462,7 +461,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 462 461 | 
             
                end
         | 
| 463 462 |  | 
| 464 463 | 
             
                it "respond true to data_source_exists?" do
         | 
| 465 | 
            -
                  assert SSTestStringDefaultsView. | 
| 464 | 
            +
                  assert SSTestStringDefaultsView.lease_connection.data_source_exists?(SSTestStringDefaultsView.table_name)
         | 
| 466 465 | 
             
                end
         | 
| 467 466 |  | 
| 468 467 | 
             
                # That have more than 4000 chars for their defintion
         | 
| @@ -508,7 +507,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 508 507 |  | 
| 509 508 | 
             
              describe "block writes to a database" do
         | 
| 510 509 | 
             
                def setup
         | 
| 511 | 
            -
                  @conn = ActiveRecord::Base. | 
| 510 | 
            +
                  @conn = ActiveRecord::Base.lease_connection
         | 
| 512 511 | 
             
                end
         | 
| 513 512 |  | 
| 514 513 | 
             
                def test_errors_when_an_insert_query_is_called_while_preventing_writes
         | 
| @@ -557,7 +556,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase | |
| 557 556 |  | 
| 558 557 | 
             
                it 'records can be inserted using SQL' do
         | 
| 559 558 | 
             
                  assert_difference("Alien.count", 2) do
         | 
| 560 | 
            -
                    Alien. | 
| 559 | 
            +
                    Alien.lease_connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')")
         | 
| 561 560 | 
             
                  end
         | 
| 562 561 | 
             
                end
         | 
| 563 562 | 
             
              end
         |