activerecord-jdbcsqlserver-adapter 50.1.0 → 51.0.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/.gitignore +3 -1
- data/.travis.yml +4 -5
- data/BACKERS.md +32 -0
- data/CHANGELOG.md +21 -87
- data/README.md +2 -3
- data/VERSION +1 -1
- data/activerecord-jdbcsqlserver-adapter.gemspec +3 -2
- data/appveyor.yml +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +55 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +5 -3
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +41 -18
- data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +2 -12
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +72 -52
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +15 -7
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +3 -6
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +27 -10
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +2 -2
- data/lib/activerecord-jdbcsqlserver-adapter.rb +1 -1
- data/lib/arel/visitors/sqlserver.rb +16 -3
- data/test/bin/setup.sh +19 -0
- data/test/cases/adapter_test_sqlserver.rb +17 -20
- data/test/cases/coerced_tests.rb +117 -11
- data/test/cases/column_test_sqlserver.rb +1 -1
- data/test/cases/helper_sqlserver.rb +6 -1
- data/test/cases/pessimistic_locking_test_sqlserver.rb +28 -11
- data/test/cases/schema_dumper_test_sqlserver.rb +10 -10
- data/test/cases/specific_schema_test_sqlserver.rb +0 -6
- data/test/cases/trigger_test_sqlserver.rb +31 -0
- data/test/config.yml +2 -2
- data/test/models/sqlserver/trigger.rb +7 -0
- data/test/models/sqlserver/trigger_history.rb +3 -0
- data/test/schema/sqlserver_specific_schema.rb +39 -5
- data/test/support/sql_counter_sqlserver.rb +3 -2
- metadata +23 -16
- data/RAILS5-TODO.md +0 -5
- data/lib/jdbc_mssql_driver_loader.rb +0 -22
- data/test/models/sqlserver/dot_table_name.rb +0 -3
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require 'pry'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ActiveRecord
         | 
| 2 4 | 
             
              module ConnectionAdapters
         | 
| 3 5 | 
             
                module SQLServer
         | 
| @@ -90,18 +92,6 @@ module ActiveRecord | |
| 90 92 | 
             
                      @connection.configure_connection
         | 
| 91 93 | 
             
                    end
         | 
| 92 94 |  | 
| 93 | 
            -
                    # @Overwrite
         | 
| 94 | 
            -
                    # Had some special logic and skipped using gem's internal query methods
         | 
| 95 | 
            -
                    def select_rows(sql, name = nil, binds = [])
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                      # In some cases the limit is converted to a `TOP(1)` but the bind parameter is still in the array
         | 
| 98 | 
            -
                      if !binds.empty? && sql.include?('TOP(1)')
         | 
| 99 | 
            -
                        binds = binds.delete_if {|b| b.name == 'LIMIT' }
         | 
| 100 | 
            -
                      end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                      exec_query(sql, name, binds).rows
         | 
| 103 | 
            -
                    end
         | 
| 104 | 
            -
             | 
| 105 95 | 
             
                    # Have to reset this because the default arjdbc functionality is to return false unless a level is passed in
         | 
| 106 96 | 
             
                    def supports_transaction_isolation?
         | 
| 107 97 | 
             
                      true
         | 
| @@ -16,6 +16,20 @@ module ActiveRecord | |
| 16 16 | 
             
                      end
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 | 
            +
                    def add_column_options!(sql, options)
         | 
| 20 | 
            +
                      sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
         | 
| 21 | 
            +
                      if options[:null] == false
         | 
| 22 | 
            +
                        sql << " NOT NULL"
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                      if options[:is_identity] == true
         | 
| 25 | 
            +
                        sql << " IDENTITY(1,1)"
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                      if options[:primary_key] == true
         | 
| 28 | 
            +
                        sql << " PRIMARY KEY"
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
                      sql
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 19 33 | 
             
                    def action_sql(action, dependency)
         | 
| 20 34 | 
             
                      case dependency
         | 
| 21 35 | 
             
                      when :restrict
         | 
| @@ -28,6 +42,14 @@ module ActiveRecord | |
| 28 42 | 
             
                      end
         | 
| 29 43 | 
             
                    end
         | 
| 30 44 |  | 
| 45 | 
            +
                    def options_include_default?(options)
         | 
| 46 | 
            +
                      super || options_primary_key_with_nil_default?(options)
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    def options_primary_key_with_nil_default?(options)
         | 
| 50 | 
            +
                      options[:primary_key] && options.include?(:default) && options[:default].nil?
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 31 53 | 
             
                  end
         | 
| 32 54 | 
             
                end
         | 
| 33 55 | 
             
              end
         | 
| @@ -3,13 +3,34 @@ module ActiveRecord | |
| 3 3 | 
             
                module SQLServer
         | 
| 4 4 | 
             
                  module SchemaDumper
         | 
| 5 5 |  | 
| 6 | 
            +
                    SQLSEVER_NO_LIMIT_TYPES = [
         | 
| 7 | 
            +
                      'text',
         | 
| 8 | 
            +
                      'ntext',
         | 
| 9 | 
            +
                      'varchar(max)',
         | 
| 10 | 
            +
                      'nvarchar(max)',
         | 
| 11 | 
            +
                      'varbinary(max)'
         | 
| 12 | 
            +
                    ].freeze
         | 
| 13 | 
            +
             | 
| 6 14 | 
             
                    private
         | 
| 7 15 |  | 
| 16 | 
            +
                    def explicit_primary_key_default?(column)
         | 
| 17 | 
            +
                      column.is_primary? && !column.is_identity?
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    def schema_limit(column)
         | 
| 21 | 
            +
                      return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type)
         | 
| 22 | 
            +
                      super
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 8 25 | 
             
                    def schema_collation(column)
         | 
| 9 26 | 
             
                      return unless column.collation
         | 
| 10 27 | 
             
                      column.collation if column.collation != collation
         | 
| 11 28 | 
             
                    end
         | 
| 12 29 |  | 
| 30 | 
            +
                    def default_primary_key?(column)
         | 
| 31 | 
            +
                      super && column.is_primary? && column.is_identity?
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 13 34 | 
             
                  end
         | 
| 14 35 | 
             
                end
         | 
| 15 36 | 
             
              end
         | 
| @@ -7,35 +7,6 @@ module ActiveRecord | |
| 7 7 | 
             
                      @native_database_types ||= initialize_native_database_types.freeze
         | 
| 8 8 | 
             
                    end
         | 
| 9 9 |  | 
| 10 | 
            -
                    def tables(name = nil)
         | 
| 11 | 
            -
                      ActiveSupport::Deprecation.warn 'Passing arguments to #tables is deprecated without replacement.' if name
         | 
| 12 | 
            -
                      tables_sql('BASE TABLE')
         | 
| 13 | 
            -
                    end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                    def table_exists?(table_name)
         | 
| 16 | 
            -
                      ActiveSupport::Deprecation.warn(<<-MSG.squish)
         | 
| 17 | 
            -
                        #table_exists? currently checks both tables and views.
         | 
| 18 | 
            -
                        This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
         | 
| 19 | 
            -
                        Use #data_source_exists? instead.
         | 
| 20 | 
            -
                      MSG
         | 
| 21 | 
            -
                      data_source_exists?(table_name)
         | 
| 22 | 
            -
                    end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    def data_source_exists?(table_name)
         | 
| 25 | 
            -
                      return false if table_name.blank?
         | 
| 26 | 
            -
                      unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object
         | 
| 27 | 
            -
                      super(unquoted_table_name)
         | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                    def views
         | 
| 31 | 
            -
                      tables_sql('VIEW')
         | 
| 32 | 
            -
                    end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                    def view_exists?(table_name)
         | 
| 35 | 
            -
                      identifier = SQLServer::Utils.extract_identifiers(table_name)
         | 
| 36 | 
            -
                      super(identifier.object)
         | 
| 37 | 
            -
                    end
         | 
| 38 | 
            -
             | 
| 39 10 | 
             
                    def create_table(table_name, comment: nil, **options)
         | 
| 40 11 | 
             
                      res = super
         | 
| 41 12 | 
             
                      clear_cache!
         | 
| @@ -43,7 +14,18 @@ module ActiveRecord | |
| 43 14 | 
             
                    end
         | 
| 44 15 |  | 
| 45 16 | 
             
                    def drop_table(table_name, options = {})
         | 
| 46 | 
            -
                       | 
| 17 | 
            +
                      # Mimic CASCADE option as best we can.
         | 
| 18 | 
            +
                      if options[:force] == :cascade
         | 
| 19 | 
            +
                        execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
         | 
| 20 | 
            +
                          fktable = fkdata['FKTABLE_NAME']
         | 
| 21 | 
            +
                          fkcolmn = fkdata['FKCOLUMN_NAME']
         | 
| 22 | 
            +
                          pktable = fkdata['PKTABLE_NAME']
         | 
| 23 | 
            +
                          pkcolmn = fkdata['PKCOLUMN_NAME']
         | 
| 24 | 
            +
                          remove_foreign_key fktable, name: fkdata['FK_NAME']
         | 
| 25 | 
            +
                          do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )"
         | 
| 26 | 
            +
                        end
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                      if options[:if_exists] && @version_year < 2016
         | 
| 47 29 | 
             
                        execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
         | 
| 48 30 | 
             
                      else
         | 
| 49 31 | 
             
                        super
         | 
| @@ -103,10 +85,36 @@ module ActiveRecord | |
| 103 85 | 
             
                    end
         | 
| 104 86 |  | 
| 105 87 | 
             
                    def primary_keys(table_name)
         | 
| 106 | 
            -
                      primaries =  | 
| 88 | 
            +
                      primaries = primary_keys_select(table_name)
         | 
| 107 89 | 
             
                      primaries.present? ? primaries : identity_columns(table_name).map(&:name)
         | 
| 108 90 | 
             
                    end
         | 
| 109 91 |  | 
| 92 | 
            +
                    def primary_keys_select(table_name)
         | 
| 93 | 
            +
                      identifier = database_prefix_identifier(table_name)
         | 
| 94 | 
            +
                      database = identifier.fully_qualified_database_quoted
         | 
| 95 | 
            +
                      sql = %{
         | 
| 96 | 
            +
                        SELECT KCU.COLUMN_NAME AS [name]
         | 
| 97 | 
            +
                        FROM #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
         | 
| 98 | 
            +
                        LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
         | 
| 99 | 
            +
                          ON KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
         | 
| 100 | 
            +
                          AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
         | 
| 101 | 
            +
                          AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
         | 
| 102 | 
            +
                          AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
         | 
| 103 | 
            +
                          AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
         | 
| 104 | 
            +
                        WHERE KCU.TABLE_NAME = #{prepared_statements ? COLUMN_DEFINITION_BIND_STRING_0 : quote(identifier.object)}
         | 
| 105 | 
            +
                        AND KCU.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? COLUMN_DEFINITION_BIND_STRING_1 : quote(identifier.schema))}
         | 
| 106 | 
            +
                        AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
         | 
| 107 | 
            +
                        ORDER BY KCU.ORDINAL_POSITION ASC
         | 
| 108 | 
            +
                      }.gsub(/[[:space:]]/, ' ')
         | 
| 109 | 
            +
                      binds = []
         | 
| 110 | 
            +
                      if prepared_statements
         | 
| 111 | 
            +
                        nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
         | 
| 112 | 
            +
                        binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128)
         | 
| 113 | 
            +
                        binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank?
         | 
| 114 | 
            +
                      end
         | 
| 115 | 
            +
                      sp_executesql(sql, 'SCHEMA', binds).map { |r| r['name'] }
         | 
| 116 | 
            +
                    end
         | 
| 117 | 
            +
             | 
| 110 118 | 
             
                    def rename_table(table_name, new_name)
         | 
| 111 119 | 
             
                      do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
         | 
| 112 120 | 
             
                      rename_table_indexes(table_name, new_name)
         | 
| @@ -130,9 +138,11 @@ module ActiveRecord | |
| 130 138 | 
             
                        remove_indexes(table_name, column_name)
         | 
| 131 139 | 
             
                      end
         | 
| 132 140 | 
             
                      sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
         | 
| 133 | 
            -
                      sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
         | 
| 134 | 
            -
                      sql_commands | 
| 135 | 
            -
                      if  | 
| 141 | 
            +
                      sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
         | 
| 142 | 
            +
                      sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false
         | 
| 143 | 
            +
                      if options.key?(:default) && default_constraint_name(table_name, column_name).present?
         | 
| 144 | 
            +
                        change_column_default(table_name, column_name, options[:default])
         | 
| 145 | 
            +
                      elsif options_include_default?(options)
         | 
| 136 146 | 
             
                        sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}"
         | 
| 137 147 | 
             
                      end
         | 
| 138 148 | 
             
                      # Add any removed indexes back
         | 
| @@ -140,6 +150,7 @@ module ActiveRecord | |
| 140 150 | 
             
                        sql_commands << "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index.columns.map { |c| quote_column_name(c) }.join(', ')})"
         | 
| 141 151 | 
             
                      end
         | 
| 142 152 | 
             
                      sql_commands.each { |c| do_execute(c) }
         | 
| 153 | 
            +
                      clear_cache!
         | 
| 143 154 | 
             
                    end
         | 
| 144 155 |  | 
| 145 156 | 
             
                    def change_column_default(table_name, column_name, default_or_changes)
         | 
| @@ -194,7 +205,7 @@ module ActiveRecord | |
| 194 205 | 
             
                      end
         | 
| 195 206 | 
             
                    end
         | 
| 196 207 |  | 
| 197 | 
            -
                    def type_to_sql(type, limit  | 
| 208 | 
            +
                    def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
         | 
| 198 209 | 
             
                      type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
         | 
| 199 210 | 
             
                      limit = nil unless type_limitable
         | 
| 200 211 | 
             
                      case type.to_s
         | 
| @@ -240,19 +251,40 @@ module ActiveRecord | |
| 240 251 | 
             
                      if !allow_null.nil? && allow_null == false && !default.nil?
         | 
| 241 252 | 
             
                        do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
         | 
| 242 253 | 
             
                      end
         | 
| 243 | 
            -
                      sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, column.limit, column.precision, column.scale}"
         | 
| 254 | 
            +
                      sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}"
         | 
| 244 255 | 
             
                      sql << ' NOT NULL' if !allow_null.nil? && allow_null == false
         | 
| 245 256 | 
             
                      do_execute sql
         | 
| 246 257 | 
             
                    end
         | 
| 247 258 |  | 
| 259 | 
            +
                    private
         | 
| 248 260 |  | 
| 249 | 
            -
                     | 
| 261 | 
            +
                    def data_source_sql(name = nil, type: nil)
         | 
| 262 | 
            +
                      scope = quoted_scope name, type: type
         | 
| 263 | 
            +
                      table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
         | 
| 264 | 
            +
                      sql = "SELECT #{table_name}"
         | 
| 265 | 
            +
                      sql << ' FROM INFORMATION_SCHEMA.TABLES'
         | 
| 266 | 
            +
                      sql << ' WHERE TABLE_CATALOG = DB_NAME()'
         | 
| 267 | 
            +
                      sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
         | 
| 268 | 
            +
                      sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
         | 
| 269 | 
            +
                      sql << " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
         | 
| 270 | 
            +
                      sql << " ORDER BY #{table_name}"
         | 
| 271 | 
            +
                      sql
         | 
| 272 | 
            +
                    end
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                    def quoted_scope(name = nil, type: nil)
         | 
| 275 | 
            +
                      identifier = SQLServer::Utils.extract_identifiers(name)
         | 
| 276 | 
            +
                      {}.tap do |scope|
         | 
| 277 | 
            +
                        scope[:schema] = identifier.schema || 'dbo'
         | 
| 278 | 
            +
                        scope[:name] = identifier.object if identifier.object
         | 
| 279 | 
            +
                        scope[:type] = type if type
         | 
| 280 | 
            +
                      end
         | 
| 281 | 
            +
                    end
         | 
| 250 282 |  | 
| 251 283 | 
             
                    # === SQLServer Specific ======================================== #
         | 
| 252 284 |  | 
| 253 285 | 
             
                    def initialize_native_database_types
         | 
| 254 286 | 
             
                      {
         | 
| 255 | 
            -
                        primary_key: ' | 
| 287 | 
            +
                        primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
         | 
| 256 288 | 
             
                        primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED',
         | 
| 257 289 | 
             
                        integer: { name: 'int', limit: 4 },
         | 
| 258 290 | 
             
                        bigint: { name: 'bigint' },
         | 
| @@ -286,21 +318,11 @@ module ActiveRecord | |
| 286 318 | 
             
                      }
         | 
| 287 319 | 
             
                    end
         | 
| 288 320 |  | 
| 289 | 
            -
                    def tables_sql(type)
         | 
| 290 | 
            -
                      tn  = lowercase_schema_reflection_sql 'TABLE_NAME'
         | 
| 291 | 
            -
                      sql = "SELECT #{tn} FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = '#{type}' ORDER BY TABLE_NAME"
         | 
| 292 | 
            -
                      select_values sql, 'SCHEMA'
         | 
| 293 | 
            -
                    end
         | 
| 294 | 
            -
             | 
| 295 321 | 
             
                    COLUMN_DEFINITION_BIND_STRING_0 = defined?(JRUBY_VERSION) ? '?' : '@0'
         | 
| 296 322 | 
             
                    COLUMN_DEFINITION_BIND_STRING_1 = defined?(JRUBY_VERSION) ? '?' : '@1'
         | 
| 297 323 |  | 
| 298 324 | 
             
                    def column_definitions(table_name)
         | 
| 299 | 
            -
                      identifier | 
| 300 | 
            -
                        SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}")
         | 
| 301 | 
            -
                      else
         | 
| 302 | 
            -
                        SQLServer::Utils.extract_identifiers(table_name)
         | 
| 303 | 
            -
                      end
         | 
| 325 | 
            +
                      identifier  = database_prefix_identifier(table_name)
         | 
| 304 326 | 
             
                      database    = identifier.fully_qualified_database_quoted
         | 
| 305 327 | 
             
                      view_exists = view_exists?(table_name)
         | 
| 306 328 | 
             
                      view_tblnm  = view_table_name(table_name) if view_exists
         | 
| @@ -482,7 +504,7 @@ module ActiveRecord | |
| 482 504 | 
             
                      @view_information ||= {}
         | 
| 483 505 | 
             
                      @view_information[table_name] ||= begin
         | 
| 484 506 | 
             
                        identifier = SQLServer::Utils.extract_identifiers(table_name)
         | 
| 485 | 
            -
                        view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME =  | 
| 507 | 
            +
                        view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA'
         | 
| 486 508 | 
             
                        if view_info
         | 
| 487 509 | 
             
                          view_info = view_info.with_indifferent_access
         | 
| 488 510 | 
             
                          if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
         | 
| @@ -505,8 +527,6 @@ module ActiveRecord | |
| 505 527 | 
             
                      match_data ? match_data[1] : column_name
         | 
| 506 528 | 
             
                    end
         | 
| 507 529 |  | 
| 508 | 
            -
                    private
         | 
| 509 | 
            -
             | 
| 510 530 | 
             
                    def create_table_definition(*args)
         | 
| 511 531 | 
             
                      SQLServer::TableDefinition.new(*args)
         | 
| 512 532 | 
             
                    end
         | 
| @@ -5,10 +5,13 @@ module ActiveRecord | |
| 5 5 | 
             
                  module ColumnMethods
         | 
| 6 6 |  | 
| 7 7 | 
             
                    def primary_key(name, type = :primary_key, **options)
         | 
| 8 | 
            -
                       | 
| 9 | 
            -
             | 
| 10 | 
            -
                       | 
| 11 | 
            -
             | 
| 8 | 
            +
                      if [:integer, :bigint].include?(type)
         | 
| 9 | 
            +
                        options[:is_identity] = true unless options.key?(:default)
         | 
| 10 | 
            +
                      elsif type == :uuid
         | 
| 11 | 
            +
                        options[:default] = options.fetch(:default, 'NEWID()')
         | 
| 12 | 
            +
                        options[:primary_key] = true
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                      super
         | 
| 12 15 | 
             
                    end
         | 
| 13 16 |  | 
| 14 17 | 
             
                    def primary_key_nonclustered(*args, **options)
         | 
| @@ -98,9 +101,14 @@ module ActiveRecord | |
| 98 101 | 
             
                  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
         | 
| 99 102 | 
             
                    include ColumnMethods
         | 
| 100 103 |  | 
| 101 | 
            -
                    def new_column_definition(name, type, options)
         | 
| 102 | 
            -
                       | 
| 103 | 
            -
                       | 
| 104 | 
            +
                    def new_column_definition(name, type, **options)
         | 
| 105 | 
            +
                      case type
         | 
| 106 | 
            +
                      when :datetime
         | 
| 107 | 
            +
                        type = :datetime2 if options[:precision]
         | 
| 108 | 
            +
                      when :primary_key
         | 
| 109 | 
            +
                        options[:is_identity] = true
         | 
| 110 | 
            +
                      end
         | 
| 111 | 
            +
                      super
         | 
| 104 112 | 
             
                    end
         | 
| 105 113 | 
             
                  end
         | 
| 106 114 |  | 
| @@ -51,7 +51,9 @@ module ActiveRecord | |
| 51 51 | 
             
                      private
         | 
| 52 52 |  | 
| 53 53 | 
             
                      def fast_string_to_time(string)
         | 
| 54 | 
            -
                         | 
| 54 | 
            +
                        time = ActiveSupport::TimeZone['UTC'].strptime(string, fast_string_to_time_format)
         | 
| 55 | 
            +
                        new_time(time.year, time.month, time.day, time.hour,
         | 
| 56 | 
            +
                                 time.min, time.sec, Rational(time.nsec, 1_000))
         | 
| 55 57 | 
             
                      rescue ArgumentError
         | 
| 56 58 | 
             
                        super
         | 
| 57 59 | 
             
                      end
         | 
| @@ -59,11 +61,6 @@ module ActiveRecord | |
| 59 61 | 
             
                      def fast_string_to_time_format
         | 
| 60 62 | 
             
                        "#{::Time::DATE_FORMATS[:_sqlserver_datetime]}.%N".freeze
         | 
| 61 63 | 
             
                      end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                      def fast_string_to_time_zone
         | 
| 64 | 
            -
                        ::Time.zone || ActiveSupport::TimeZone['UTC']
         | 
| 65 | 
            -
                      end
         | 
| 66 | 
            -
             | 
| 67 64 | 
             
                    end
         | 
| 68 65 | 
             
                  end
         | 
| 69 66 | 
             
                end
         | 
| @@ -3,6 +3,7 @@ require 'active_record' | |
| 3 3 | 
             
            require 'arel_sqlserver'
         | 
| 4 4 | 
             
            require 'active_record/connection_adapters/abstract_adapter'
         | 
| 5 5 | 
             
            require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
         | 
| 6 | 
            +
            require 'active_record/connection_adapters/sqlserver/core_ext/calculations'
         | 
| 6 7 | 
             
            require 'active_record/connection_adapters/sqlserver/core_ext/explain' unless defined? JRUBY_VERSION
         | 
| 7 8 | 
             
            require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber'
         | 
| 8 9 | 
             
            require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods'
         | 
| @@ -56,11 +57,13 @@ module ActiveRecord | |
| 56 57 |  | 
| 57 58 | 
             
                  cattr_accessor :cs_equality_operator, instance_accessor: false
         | 
| 58 59 | 
             
                  cattr_accessor :use_output_inserted, instance_accessor: false
         | 
| 60 | 
            +
                  cattr_accessor :exclude_output_inserted_table_names, instance_accessor: false
         | 
| 59 61 | 
             
                  cattr_accessor :showplan_option, instance_accessor: false
         | 
| 60 62 | 
             
                  cattr_accessor :lowercase_schema_reflection
         | 
| 61 63 |  | 
| 62 64 | 
             
                  self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
         | 
| 63 65 | 
             
                  self.use_output_inserted = true
         | 
| 66 | 
            +
                  self.exclude_output_inserted_table_names = Concurrent::Map.new { false }
         | 
| 64 67 |  | 
| 65 68 | 
             
                  def initialize(connection, logger = nil, config = {})
         | 
| 66 69 | 
             
                    super(connection, logger, config)
         | 
| @@ -85,14 +88,6 @@ module ActiveRecord | |
| 85 88 | 
             
                    SQLServer::SchemaCreation.new self
         | 
| 86 89 | 
             
                  end
         | 
| 87 90 |  | 
| 88 | 
            -
                  def supports_migrations?
         | 
| 89 | 
            -
                    true
         | 
| 90 | 
            -
                  end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                  def supports_primary_key?
         | 
| 93 | 
            -
                    true
         | 
| 94 | 
            -
                  end
         | 
| 95 | 
            -
             | 
| 96 91 | 
             
                  def supports_ddl_transactions?
         | 
| 97 92 | 
             
                    true
         | 
| 98 93 | 
             
                  end
         | 
| @@ -159,10 +154,10 @@ module ActiveRecord | |
| 159 154 |  | 
| 160 155 | 
             
                  def disable_referential_integrity
         | 
| 161 156 | 
             
                    tables = tables_with_referential_integrity
         | 
| 162 | 
            -
                    tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" }
         | 
| 157 | 
            +
                    tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" }
         | 
| 163 158 | 
             
                    yield
         | 
| 164 159 | 
             
                  ensure
         | 
| 165 | 
            -
                    tables.each { |t| do_execute "ALTER TABLE #{t} CHECK CONSTRAINT ALL" }
         | 
| 160 | 
            +
                    tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} CHECK CONSTRAINT ALL" }
         | 
| 166 161 | 
             
                  end
         | 
| 167 162 |  | 
| 168 163 | 
             
                  # === Abstract Adapter (Connection Management) ================== #
         | 
| @@ -242,6 +237,14 @@ module ActiveRecord | |
| 242 237 | 
             
                    @connection_options[:database_prefix]
         | 
| 243 238 | 
             
                  end
         | 
| 244 239 |  | 
| 240 | 
            +
                  def database_prefix_identifier(name)
         | 
| 241 | 
            +
                    if database_prefix_remote_server?
         | 
| 242 | 
            +
                      SQLServer::Utils.extract_identifiers("#{database_prefix}#{name}")
         | 
| 243 | 
            +
                    else
         | 
| 244 | 
            +
                      SQLServer::Utils.extract_identifiers(name)
         | 
| 245 | 
            +
                    end
         | 
| 246 | 
            +
                  end
         | 
| 247 | 
            +
             | 
| 245 248 | 
             
                  def version
         | 
| 246 249 | 
             
                    self.class::VERSION
         | 
| 247 250 | 
             
                  end
         | 
| @@ -339,6 +342,20 @@ module ActiveRecord | |
| 339 342 | 
             
                      NoDatabaseError.new(message)
         | 
| 340 343 | 
             
                    when /data would be truncated/
         | 
| 341 344 | 
             
                      ValueTooLong.new(message)
         | 
| 345 | 
            +
                    when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/
         | 
| 346 | 
            +
                      pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2)
         | 
| 347 | 
            +
                      MismatchedForeignKey.new(
         | 
| 348 | 
            +
                        self,
         | 
| 349 | 
            +
                        message: message,
         | 
| 350 | 
            +
                        table: fk_id.schema,
         | 
| 351 | 
            +
                        foreign_key: fk_id.object,
         | 
| 352 | 
            +
                        target_table: pk_id.schema,
         | 
| 353 | 
            +
                        primary_key: pk_id.object
         | 
| 354 | 
            +
                      )
         | 
| 355 | 
            +
                    when /Cannot insert the value NULL into column.*does not allow nulls/
         | 
| 356 | 
            +
                      NotNullViolation.new(message)
         | 
| 357 | 
            +
                    when /Arithmetic overflow error/
         | 
| 358 | 
            +
                      RangeError.new(message)
         | 
| 342 359 | 
             
                    else
         | 
| 343 360 | 
             
                      super
         | 
| 344 361 | 
             
                    end
         |