diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7eb8439a4..8e82c9161 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,3 +33,27 @@ jobs: - name: Run tests run: docker compose run ci + + + standardrb: + name: Code linting and formatting + runs-on: ubuntu-20.04 # TODO: Change back to 'ubuntu-latest' when https://github.com/microsoft/mssql-docker/issues/899 resolved. + + env: + COMPOSE_FILE: compose.ci.yaml + + strategy: + fail-fast: false + matrix: + ruby: + - 3.4.1 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Build docker images + run: docker compose build --build-arg TARGET_VERSION=${{ matrix.ruby }} + + - name: Run standardrb + run: docker compose run standardrb diff --git a/Gemfile b/Gemfile index 341d6786b..f2a7b8885 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec gem "bcrypt" -gem "pg", ">= 0.18.0" +gem "pg", ">= 0.18.0" gem "sqlite3", ">= 1.6.6" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "benchmark-ips" @@ -27,14 +27,14 @@ else require "net/http" require "yaml" - spec = eval(File.read("activerecord-sqlserver-adapter.gemspec")) - ver = spec.dependencies.detect { |d| d.name == "activerecord" }.requirement.requirements.first.last.version + spec = Gem::Specification.load("activerecord-sqlserver-adapter.gemspec") + ver = spec.dependencies.detect { |d| d.name == "activerecord" }.requirement.requirements.first.last.version major, minor, _tiny, pre = ver.split(".") if pre ver else - uri = URI.parse("https://rubygems.org/api/v1/versions/activerecord.yaml") + uri = URI.parse("https://rubygems.org/api/v1/versions/activerecord.yaml") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE @@ -68,9 +68,7 @@ end group :guard do gem "guard" gem "guard-minitest" - gem "terminal-notifier-guard" if RbConfig::CONFIG["host_os"] =~ /darwin/ + gem "terminal-notifier-guard" if /darwin/.match?(RbConfig::CONFIG["host_os"]) end -group :rubocop do - gem "rubocop", require: false -end +gem "standard", require: false diff --git a/Guardfile b/Guardfile index ab781a114..17f008db2 100644 --- a/Guardfile +++ b/Guardfile @@ -6,7 +6,7 @@ clearing :on notification :terminal_notifier if defined?(TerminalNotifier) ignore %r{debug\.log} -ar_lib = File.join ARTest::SQLServer.root_activerecord, "lib" +ar_lib = File.join ARTest::SQLServer.root_activerecord, "lib" ar_test = File.join ARTest::SQLServer.root_activerecord, "test" guard :minitest, { @@ -24,7 +24,7 @@ guard :minitest, { else watch(%r{^test/cases/\w+_test_sqlserver\.rb$}) watch(%r{^test/cases/coerced_tests\.rb$}) { "test/cases/coerced_tests.rb" } - watch(%r{^lib/active_record/connection_adapters/sqlserver/([^/]+)\.rb$}) { |m| "test/cases/#{m[1]}_test_sqlserver.rb" } + watch(%r{^lib/active_record/connection_adapters/sqlserver/([^/]+)\.rb$}) { |m| "test/cases/#{m[1]}_test_sqlserver.rb" } watch(%r{^test/cases/helper_sqlserver\.rb$}) { "test" } end end diff --git a/Rakefile b/Rakefile index 3c6f58630..8e7c505e8 100644 --- a/Rakefile +++ b/Rakefile @@ -11,7 +11,7 @@ task default: [:test] namespace :test do ENV["ARCONN"] = "sqlserver" - %w(dblib).each do |mode| + %w[dblib].each do |mode| Rake::TestTask.new(mode) do |t| t.libs = ARTest::SQLServer.test_load_paths t.test_files = test_files diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 071a2ceb9..5cbe0a3a7 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -3,28 +3,27 @@ version = File.read(File.expand_path("VERSION", __dir__)).strip Gem::Specification.new do |spec| - spec.name = "activerecord-sqlserver-adapter" - spec.platform = Gem::Platform::RUBY - spec.version = version + spec.name = "activerecord-sqlserver-adapter" + spec.platform = Gem::Platform::RUBY + spec.version = version spec.required_ruby_version = ">= 3.2.0" - spec.license = "MIT" - spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward", "Aidan Haran"] - spec.email = ["ken@metaskills.net", "will@wbond.net"] - spec.homepage = "http://github.com/rails-sqlserver/activerecord-sqlserver-adapter" - spec.summary = "ActiveRecord SQL Server Adapter." - spec.description = "ActiveRecord SQL Server Adapter. SQL Server 2012 and upward." + spec.license = "MIT" + spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward", "Aidan Haran"] + spec.email = ["ken@metaskills.net", "will@wbond.net"] + spec.homepage = "http://github.com/rails-sqlserver/activerecord-sqlserver-adapter" + spec.summary = "ActiveRecord SQL Server Adapter." + spec.description = "ActiveRecord SQL Server Adapter. SQL Server 2012 and upward." - spec.metadata = { + spec.metadata = { "bug_tracker_uri" => "https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues", "changelog_uri" => "https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v#{version}/CHANGELOG.md", - "source_code_uri" => "https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v#{version}", + "source_code_uri" => "https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v#{version}" } - spec.files = `git ls-files -z`.split("\x0") - spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } - spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.files = `git ls-files -z`.split("\x0") + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ["lib"] spec.add_dependency "activerecord", "~> 8.1.0.alpha" diff --git a/compose.ci.yaml b/compose.ci.yaml index f339681e7..1e02f057d 100644 --- a/compose.ci.yaml +++ b/compose.ci.yaml @@ -12,3 +12,10 @@ services: command: wait-for sqlserver:1433 -- bundle exec rake test depends_on: - "sqlserver" + standardrb: + environment: + - RAILS_BRANCH=main + build: + context: . + dockerfile: Dockerfile.ci + command: bundle exec standardrb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb index 639874a81..f65400a87 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb @@ -15,7 +15,7 @@ def attributes_for_update(attribute_names) super(attribute_names).reject do |name| column = self.class.columns_hash[name] - column && column.respond_to?(:is_identity?) && column.is_identity? + column&.respond_to?(:is_identity?) && column.is_identity? end end end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb index d650d6d66..137e17c5d 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -32,8 +32,7 @@ def unprepare_sqlserver_statement(sql, binds) executesql = executesql.match(SQLSERVER_STATEMENT_REGEXP).to_a[1] binds.each_with_index do |bind, index| - - value = if bind.is_a?(::ActiveModel::Attribute) then + value = if bind.is_a?(::ActiveModel::Attribute) connection.quote(bind.value_for_database) else connection.quote(bind) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb index 8c5fc8a85..ec508347b 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb @@ -24,16 +24,16 @@ def construct_relation_for_exists(conditions) def _construct_relation_for_exists(conditions) conditions = sanitize_forbidden_attributes(conditions) - if distinct_value && offset_value + relation = if distinct_value && offset_value # Start of monkey-patch if select_values.present? - relation = order(*select_values).limit!(1) + order(*select_values).limit!(1) else - relation = except(:order).limit!(1) + except(:order).limit!(1) end # End of monkey-patch else - relation = except(:select, :distinct, :order)._select!(Arel.sql(::ActiveRecord::FinderMethods::ONE_AS_ONE, retryable: true)).limit!(1) + except(:select, :distinct, :order)._select!(Arel.sql(::ActiveRecord::FinderMethods::ONE_AS_ONE, retryable: true)).limit!(1) end case conditions diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 7857b6eae..217df0026 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -14,16 +14,18 @@ def write_query?(sql) # :nodoc: end def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:) - result, affected_rows = 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) + id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name, raw_connection) do - internal_exec_sql_query(sql, raw_connection) - end - else - internal_exec_sql_query(sql, raw_connection) - end + result, affected_rows = if id_insert_table_name + # 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, raw_connection) do + internal_exec_sql_query(sql, raw_connection) + end + else + internal_exec_sql_query(sql, raw_connection) + end verified! notification_payload[:affected_rows] = affected_rows @@ -41,16 +43,12 @@ def cast_result(raw_result) # Returns the affected rows from results. def affected_rows(raw_result) - raw_result&.first&.fetch('AffectedRows', nil) + raw_result&.first&.fetch("AffectedRows", nil) end # Returns the affected rows from results or handle. def affected_rows_from_results_or_handle(raw_result, handle) - if affected_rows_from_result = affected_rows(raw_result) - affected_rows_from_result - else - handle.affected_rows - end + affected_rows(raw_result) || handle.affected_rows end def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false) @@ -66,19 +64,19 @@ def internal_exec_sql_query(sql, conn) handle = internal_raw_execute(sql, conn) results = handle_to_names_and_values(handle, ar_result: true) - return results, affected_rows_from_results_or_handle(results, handle) + [results, affected_rows_from_results_or_handle(results, handle)] ensure finish_statement_handle(handle) end def exec_delete(sql, name = nil, binds = []) sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" - super(sql, name, binds) + super end def exec_update(sql, name = nil, binds = []) sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" - super(sql, name, binds) + super end def begin_db_transaction @@ -155,14 +153,16 @@ def default_insert_value(column) private :default_insert_value def build_insert_sql(insert) # :nodoc: - sql = +"INSERT #{insert.into}" + sql = "INSERT #{insert.into}" + + returning = insert.send(:insert_all).returning - if returning = insert.send(:insert_all).returning + if returning returning_sql = if returning.is_a?(String) - returning - else - Array(returning).map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") - end + returning + else + Array(returning).map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + end sql << " OUTPUT #{returning_sql}" end @@ -174,17 +174,17 @@ def build_insert_sql(insert) # :nodoc: def execute_procedure(proc_name, *variables) vars = if variables.any? && variables.first.is_a?(Hash) - variables.first.map { |k, v| "@#{k} = #{quote(v)}" } - else - variables.map { |v| quote(v) } - end.join(", ") + variables.first.map { |k, v| "@#{k} = #{quote(v)}" } + else + variables.map { |v| quote(v) } + end.join(", ") sql = "EXEC #{proc_name} #{vars}".strip log(sql, "Execute Procedure") do |notification_payload| with_raw_connection do |conn| result = internal_raw_execute(sql, conn) verified! - options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc } + options = {as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc} result.each(options) do |row| r = row.with_indifferent_access @@ -218,7 +218,7 @@ def user_options rows = select_rows("DBCC USEROPTIONS WITH NO_INFOMSGS", "SCHEMA") rows = rows.first if rows.size == 2 && rows.last.empty? - rows.reduce(HashWithIndifferentAccess.new) do |values, row| + rows.each_with_object(HashWithIndifferentAccess.new) do |row, values| if row.instance_of? Hash set_option = row.values[0].gsub(/\s+/, "_") user_value = row.values[1] @@ -227,7 +227,6 @@ def user_options user_value = row[1] end values[set_option] = user_value - values end end @@ -281,35 +280,35 @@ def sql_for_insert(sql, pk, binds, returning) end sql = if pk && use_output_inserted? && !database_prefix_remote_server? - table_name ||= get_table_name(sql) - exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) - - if exclude_output_inserted - pk_and_types = Array(pk).map do |subkey| - { - quoted: SQLServer::Utils.extract_identifiers(subkey).quoted, - id_sql_type: exclude_output_inserted_id_sql_type(subkey, exclude_output_inserted) - } - end - - <<~SQL.squish - DECLARE @ssaIdInsertTable table (#{pk_and_types.map { |pk_and_type| "#{pk_and_type[:quoted]} #{pk_and_type[:id_sql_type]}"}.join(", ") }); - #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT #{ pk_and_types.map { |pk_and_type| "INSERTED.#{pk_and_type[:quoted]}" }.join(", ") } INTO @ssaIdInsertTable"} - SELECT #{pk_and_types.map {|pk_and_type| "CAST(#{pk_and_type[:quoted]} AS #{pk_and_type[:id_sql_type]}) #{pk_and_type[:quoted]}"}.join(", ")} FROM @ssaIdInsertTable - SQL - else - returning_columns = returning || Array(pk) - - if returning_columns.any? - returning_columns_statements = returning_columns.map { |c| " INSERTED.#{SQLServer::Utils.extract_identifiers(c).quoted}" } - sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT" + returning_columns_statements.join(",") - else - sql - end - end - else - "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" - end + table_name ||= get_table_name(sql) + exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) + + if exclude_output_inserted + pk_and_types = Array(pk).map do |subkey| + { + quoted: SQLServer::Utils.extract_identifiers(subkey).quoted, + id_sql_type: exclude_output_inserted_id_sql_type(subkey, exclude_output_inserted) + } + end + + <<~SQL.squish + DECLARE @ssaIdInsertTable table (#{pk_and_types.map { |pk_and_type| "#{pk_and_type[:quoted]} #{pk_and_type[:id_sql_type]}" }.join(", ")}); + #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT #{pk_and_types.map { |pk_and_type| "INSERTED.#{pk_and_type[:quoted]}" }.join(", ")} INTO @ssaIdInsertTable"} + SELECT #{pk_and_types.map { |pk_and_type| "CAST(#{pk_and_type[:quoted]} AS #{pk_and_type[:id_sql_type]}) #{pk_and_type[:quoted]}" }.join(", ")} FROM @ssaIdInsertTable + SQL + else + returning_columns = returning || Array(pk) + + if returning_columns.any? + returning_columns_statements = returning_columns.map { |c| " INSERTED.#{SQLServer::Utils.extract_identifiers(c).quoted}" } + sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT" + returning_columns_statements.join(",") + else + sql + end + end + else + "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" + end [sql, binds] end @@ -317,9 +316,9 @@ def sql_for_insert(sql, pk, binds, returning) # === SQLServer Specific ======================================== # def set_identity_insert(table_name, conn, enable) - internal_raw_execute("SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}", conn , perform_do: true) - rescue Exception - raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" + internal_raw_execute("SET IDENTITY_INSERT #{table_name} #{enable ? "ON" : "OFF"}", conn, perform_do: true) + rescue + raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? "ON" : "OFF"} for table #{table_name}" end # === SQLServer Specific (Executing) ============================ # @@ -350,9 +349,9 @@ def sp_executesql_sql_type(attr) value = active_model_attribute?(attr) ? attr.value_for_database : attr if value.is_a?(Numeric) - value > 2_147_483_647 ? "bigint".freeze : "int".freeze + (value > 2_147_483_647) ? "bigint" : "int" else - "nvarchar(max)".freeze + "nvarchar(max)" end end @@ -418,7 +417,7 @@ def query_requires_identity_insert?(sql) raw_table_name = get_raw_table_name(sql) id_column = identity_columns(raw_table_name).first - id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? SQLServer::Utils.extract_identifiers(raw_table_name).quoted : false + (id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i) ? SQLServer::Utils.extract_identifiers(raw_table_name).quoted : false end def insert_sql?(sql) @@ -457,7 +456,7 @@ def handle_to_names_and_values(handle, options = {}) end def finish_statement_handle(handle) - handle.cancel if handle + handle&.cancel handle end diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index 1a22b23e6..28bc15da7 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -32,20 +32,19 @@ def collation private def create_database_options(options = {}) - keys = [:collate] + keys = [:collate] copts = @connection_parameters - options = { + { collate: copts[:collation] }.merge(options.symbolize_keys).select { |_, v| v.present? }.slice(*keys).map { |k, v| "#{k.to_s.upcase} #{v}" }.join(" ") - options end def create_database_edition_options(options = {}) - keys = [:maxsize, :edition, :service_objective] + keys = [:maxsize, :edition, :service_objective] copts = @connection_parameters edition_options = { maxsize: copts[:azure_maxsize], diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 3fe889bd5..5f609aaa0 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -55,11 +55,11 @@ def fetch_type_metadata(sql_type, sqlserver_options = {}) cast_type = lookup_cast_type(sql_type) simple_type = SqlTypeMetadata.new( - sql_type: sql_type, - type: cast_type.type, - limit: cast_type.limit, + sql_type: sql_type, + type: cast_type.type, + limit: cast_type.limit, precision: cast_type.precision, - scale: cast_type.scale + scale: cast_type.scale ) SQLServer::TypeMetadata.new(simple_type, **sqlserver_options) @@ -79,7 +79,7 @@ def quote_string_single_national(s) def quote_default_expression(value, column) cast_type = lookup_cast_type(column.sql_type) - if cast_type.type == :uuid && value.is_a?(String) && value.include?('()') + if cast_type.type == :uuid && value.is_a?(String) && value.include?("()") value else super @@ -87,7 +87,7 @@ def quote_default_expression(value, column) end def quoted_true - '1' + "1" end def unquoted_true @@ -95,7 +95,7 @@ def unquoted_true end def quoted_false - '0' + "0" end def unquoted_false diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 781b0f0ed..9627a4fc5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -36,19 +36,23 @@ def drop_table(*table_names, **options) end def indexes(table_name) - data = select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") rescue [] + data = begin + select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") + rescue + [] + end data.reduce([]) do |indexes, index| - if index['index_description'].match?(/primary key/) + if index["index_description"].match?(/primary key/) indexes else - name = index['index_name'] - unique = index['index_description'].match?(/unique/) - where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}", "SCHEMA") - orders = {} + name = index["index_name"] + unique = index["index_description"].match?(/unique/) + where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}", "SCHEMA") + orders = {} columns = [] - index['index_keys'].split(",").each do |column| + index["index_keys"].split(",").each do |column| column.strip! if column.end_with?("(-)") @@ -107,8 +111,8 @@ def primary_keys(table_name) def primary_keys_select(table_name) identifier = database_prefix_identifier(table_name) database = identifier.fully_qualified_database_quoted - sql = %{ - SELECT #{lowercase_schema_reflection_sql('KCU.COLUMN_NAME')} AS [name] + sql = %( + SELECT #{lowercase_schema_reflection_sql("KCU.COLUMN_NAME")} AS [name] FROM #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME @@ -116,11 +120,15 @@ def primary_keys_select(table_name) AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' - WHERE KCU.TABLE_NAME = #{prepared_statements ? '@0' : quote(identifier.object)} - AND KCU.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))} + WHERE KCU.TABLE_NAME = #{prepared_statements ? "@0" : quote(identifier.object)} + AND KCU.TABLE_SCHEMA = #{if identifier.schema.blank? + "schema_name()" + else + (prepared_statements ? "@1" : quote(identifier.schema)) + end} AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' ORDER BY KCU.ORDINAL_POSITION ASC - }.gsub(/[[:space:]]/, " ") + ).gsub(/[[:space:]]/, " ") binds = [] nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 @@ -163,10 +171,10 @@ def change_column(table_name, column_name, type, options = {}) column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } without_constraints = options.key?(:default) || options.key?(:limit) default = if !options.key?(:default) && column_object - column_object.default - else - options[:default] - end + column_object.default + else + options[:default] + end if without_constraints || (column_object && column_object.type != type.to_sym) remove_default_constraint(table_name, column_name) @@ -187,7 +195,7 @@ def change_column(table_name, column_name, type, options = {}) # Add any removed indexes back indexes.each do |index| - sql_commands << "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index.columns.map { |c| quote_column_name(c) }.join(', ')})" + sql_commands << "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index.columns.map { |c| quote_column_name(c) }.join(", ")})" end sql_commands.each { |c| execute(c) } @@ -294,16 +302,16 @@ 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 binary_basic).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 when "integer" case limit - when 1 then "tinyint" - when 2 then "smallint" - when 3..4, nil then "integer" - when 5..8 then "bigint" + when 1 then "tinyint" + when 2 then "smallint" + when 3..4, nil then "integer" + when 5..8 then "bigint" else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.") end when "time" # https://learn.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql @@ -344,18 +352,18 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # In SQL Server only the first column added should have the `ADD` keyword. def add_timestamps(table_name, **options) fragments = add_timestamps_for_alter(table_name, **options) - fragments[1..].each { |fragment| fragment.sub!('ADD ', '') } - execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}" + fragments[1..].each { |fragment| fragment.sub!("ADD ", "") } + execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(", ")}" end def columns_for_distinct(columns, orders) order_columns = orders.reject(&:blank?).map { |s| - 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?) - .reject { |s| columns.include?(s) } + 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?) + .reject { |s| columns.include?(s) } order_columns_aliased = order_columns.map.with_index { |column, i| "#{column} AS alias_#{i}" } @@ -403,11 +411,11 @@ def drop_schema(schema_name) # Returns an array of schema names. def schema_names sql = <<~SQL.squish - SELECT name - FROM sys.schemas - WHERE - name NOT LIKE 'db_%' AND - name NOT IN ('INFORMATION_SCHEMA', 'sys', 'guest') + SELECT name + FROM sys.schemas + WHERE + name NOT LIKE 'db_%' AND + name NOT IN ('INFORMATION_SCHEMA', 'sys', 'guest') SQL query_values(sql, "SCHEMA") @@ -418,9 +426,9 @@ def schema_names def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type: type) - table_schema = lowercase_schema_reflection_sql('TABLE_SCHEMA') - table_name = lowercase_schema_reflection_sql('TABLE_NAME') - database = scope[:database].present? ? "#{scope[:database]}." : "" + table_schema = lowercase_schema_reflection_sql("TABLE_SCHEMA") + table_name = lowercase_schema_reflection_sql("TABLE_NAME") + database = scope[:database].present? ? "#{scope[:database]}." : "" table_catalog = scope[:database].present? ? quote(scope[:database]) : "DB_NAME()" sql = "SELECT " @@ -454,42 +462,42 @@ def initialize_native_database_types { primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY", primary_key_nonclustered: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED", - integer: { name: "int", limit: 4 }, - bigint: { name: "bigint" }, - boolean: { name: "bit" }, - decimal: { name: "decimal" }, - money: { name: "money" }, - smallmoney: { name: "smallmoney" }, - float: { name: "float" }, - real: { name: "real" }, - date: { name: "date" }, - datetime: { name: "datetime" }, - datetime2: { name: "datetime2" }, - datetimeoffset: { name: "datetimeoffset" }, - smalldatetime: { name: "smalldatetime" }, - timestamp: { name: "datetime2(6)" }, - time: { name: "time" }, - char: { name: "char" }, - varchar: { name: "varchar", limit: 8000 }, - varchar_max: { name: "varchar(max)" }, - text_basic: { name: "text" }, - nchar: { name: "nchar" }, - string: { name: "nvarchar", limit: 4000 }, - text: { name: "nvarchar(max)" }, - ntext: { name: "ntext" }, - binary_basic: { name: "binary" }, - varbinary: { name: "varbinary", limit: 8000 }, - binary: { name: "varbinary(max)" }, - uuid: { name: "uniqueidentifier" }, - ss_timestamp: { name: "timestamp" }, - json: { name: "nvarchar(max)" } + integer: {name: "int", limit: 4}, + bigint: {name: "bigint"}, + boolean: {name: "bit"}, + decimal: {name: "decimal"}, + money: {name: "money"}, + smallmoney: {name: "smallmoney"}, + float: {name: "float"}, + real: {name: "real"}, + date: {name: "date"}, + datetime: {name: "datetime"}, + datetime2: {name: "datetime2"}, + datetimeoffset: {name: "datetimeoffset"}, + smalldatetime: {name: "smalldatetime"}, + timestamp: {name: "datetime2(6)"}, + time: {name: "time"}, + char: {name: "char"}, + varchar: {name: "varchar", limit: 8000}, + varchar_max: {name: "varchar(max)"}, + text_basic: {name: "text"}, + nchar: {name: "nchar"}, + string: {name: "nvarchar", limit: 4000}, + text: {name: "nvarchar(max)"}, + ntext: {name: "ntext"}, + binary_basic: {name: "binary"}, + varbinary: {name: "varbinary", limit: 8000}, + binary: {name: "varbinary(max)"}, + uuid: {name: "uniqueidentifier"}, + ss_timestamp: {name: "timestamp"}, + json: {name: "nvarchar(max)"} } end def column_definitions(table_name) - identifier = database_prefix_identifier(table_name) - database = identifier.fully_qualified_database_quoted - view_exists = view_exists?(table_name) + identifier = database_prefix_identifier(table_name) + database = identifier.fully_qualified_database_quoted + view_exists = view_exists?(table_name) if view_exists sql = <<~SQL @@ -511,40 +519,38 @@ def column_definitions(table_name) results = internal_exec_query(sql, "SCHEMA", binds) raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if results.empty? - columns = results.map do |ci| + results.map do |ci| col = { - name: ci["name"], - numeric_scale: ci["numeric_scale"], - numeric_precision: ci["numeric_precision"], + name: ci["name"], + numeric_scale: ci["numeric_scale"], + numeric_precision: ci["numeric_precision"], datetime_precision: ci["datetime_precision"], - collation: ci["collation"], - ordinal_position: ci["ordinal_position"], - length: ci["length"] + collation: ci["collation"], + ordinal_position: ci["ordinal_position"], + length: ci["length"] } col[:table_name] = view_exists ? view_table_name(table_name) : table_name col[:type] = column_type(ci: ci) - col[:default_value], col[:default_function] = default_value_and_function(default: ci['default_value'], - name: ci['name'], - type: col[:type], - original_type: ci['type'], - view_exists: view_exists, - table_name: table_name, - default_functions: default_functions) - - col[:null] = ci['is_nullable'].to_i == 1 - col[:is_primary] = ci['is_primary'].to_i == 1 - - if [true, false].include?(ci['is_identity']) - col[:is_identity] = ci['is_identity'] + col[:default_value], col[:default_function] = default_value_and_function(default: ci["default_value"], + name: ci["name"], + type: col[:type], + original_type: ci["type"], + view_exists: view_exists, + table_name: table_name, + default_functions: default_functions) + + col[:null] = ci["is_nullable"].to_i == 1 + col[:is_primary] = ci["is_primary"].to_i == 1 + + col[:is_identity] = if [true, false].include?(ci["is_identity"]) + ci["is_identity"] else - col[:is_identity] = ci['is_identity'].to_i == 1 + ci["is_identity"].to_i == 1 end col end - - columns end def default_value_and_function(default:, name:, type:, original_type:, view_exists:, table_name:, default_functions:) @@ -566,9 +572,9 @@ def default_value_and_function(default:, name:, type:, original_type:, view_exis [nil, nil] else type = case type - when /smallint|int|bigint/ then original_type - else type - end + when /smallint|int|bigint/ then original_type + else type + end value = default.match(/\A\((.*)\)\Z/m)[1] value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA") [value, nil] @@ -576,36 +582,36 @@ def default_value_and_function(default:, name:, type:, original_type:, view_exis end def column_type(ci:) - case ci['type'] + case ci["type"] when /^bit|image|text|ntext|datetime$/ - ci['type'] + ci["type"] when /^datetime2|datetimeoffset$/i - "#{ci['type']}(#{ci['datetime_precision']})" + "#{ci["type"]}(#{ci["datetime_precision"]})" when /^time$/i - "#{ci['type']}(#{ci['datetime_precision']})" + "#{ci["type"]}(#{ci["datetime_precision"]})" when /^numeric|decimal$/i - "#{ci['type']}(#{ci['numeric_precision']},#{ci['numeric_scale']})" + "#{ci["type"]}(#{ci["numeric_precision"]},#{ci["numeric_scale"]})" when /^float|real$/i - "#{ci['type']}" + ci["type"] when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/ - ci['length'].to_i == -1 ? "#{ci['type']}(max)" : "#{ci['type']}(#{ci['length']})" + (ci["length"].to_i == -1) ? "#{ci["type"]}(max)" : "#{ci["type"]}(#{ci["length"]})" else - ci['type'] + ci["type"] end 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()" + else + prepared_statements ? "@1" : quote(identifier.schema) + end %{ SELECT - #{lowercase_schema_reflection_sql('o.name')} AS [table_name], - #{lowercase_schema_reflection_sql('c.name')} AS [name], + #{lowercase_schema_reflection_sql("o.name")} AS [table_name], + #{lowercase_schema_reflection_sql("c.name")} AS [name], t.name AS [type], d.definition AS [default_value], CASE @@ -681,7 +687,7 @@ def remove_default_constraint(table_name, column_name) execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row| row["constraint_type"] == "DEFAULT on column #{column_name}" end.each do |row| - execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}" + execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row["constraint_name"]}" end end @@ -738,18 +744,18 @@ def view_information(table_name) @view_information[table_name] ||= begin identifier = SQLServer::Utils.extract_identifiers(table_name) - information_query_table = identifier.database.present? ? "[#{identifier.database}].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]" + information_query_table = identifier.database.present? ? "[#{identifier.database}].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]" view_info = select_one("SELECT * FROM #{information_query_table} WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA").to_h if view_info.present? - if view_info['VIEW_DEFINITION'].blank? || view_info['VIEW_DEFINITION'].length == 4000 - view_info['VIEW_DEFINITION'] = begin - select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join - rescue - warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;" - nil - end + if view_info["VIEW_DEFINITION"].blank? || view_info["VIEW_DEFINITION"].length == 4000 + view_info["VIEW_DEFINITION"] = begin + select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join + rescue + warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;" + nil + end end end @@ -758,11 +764,11 @@ def view_information(table_name) end def views_real_column_name(table_name, column_name) - view_definition = view_information(table_name)['VIEW_DEFINITION'] + view_definition = view_information(table_name)["VIEW_DEFINITION"] return column_name if view_definition.blank? # Remove "CREATE VIEW ... AS SELECT ..." and then match the column name. - match_data = view_definition.sub(/CREATE\s+VIEW.*AS\s+SELECT\s/, '').match(/([\w-]*)\s+AS\s+#{column_name}\W/im) + match_data = view_definition.sub(/CREATE\s+VIEW.*AS\s+SELECT\s/, "").match(/([\w-]*)\s+AS\s+#{column_name}\W/im) match_data ? match_data[1] : column_name end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index a93a839f4..30932ee72 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -7,9 +7,9 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Showplan - OPTION_ALL = "SHOWPLAN_ALL" + OPTION_ALL = "SHOWPLAN_ALL" OPTION_TEXT = "SHOWPLAN_TEXT" - OPTION_XML = "SHOWPLAN_XML" + OPTION_XML = "SHOWPLAN_XML" OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML] def explain(arel, binds = [], options = []) @@ -30,10 +30,10 @@ def with_showplan_on end def set_showplan_option(enable = true) - sql = "SET #{showplan_option} #{enable ? 'ON' : 'OFF'}" + sql = "SET #{showplan_option} #{enable ? "ON" : "OFF"}" raw_execute(sql, "SCHEMA") - rescue Exception - raise ActiveRecordError, "#{showplan_option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?" + rescue + raise ActiveRecordError, "#{showplan_option} could not be turned #{enable ? "ON" : "OFF"}, perhaps you do not have SHOWPLAN permissions?" end def showplan_option diff --git a/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb b/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb index 287e4efb7..0395ddb42 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb @@ -36,7 +36,7 @@ def compute_column_widths result.columns.each_with_index do |column, i| cells_in_column = [column] + result.rows.map { |r| cast_item(r[i]) } computed_width = cells_in_column.map(&:length).max - final_width = computed_width > max_column_width ? max_column_width : computed_width + final_width = (computed_width > max_column_width) ? max_column_width : computed_width computed_widths << final_width end end @@ -51,7 +51,7 @@ def build_cells(items) items.each_with_index do |item, i| cells << cast_item(item).ljust(@widths[i]) end - "| #{cells.join(' | ')} |" + "| #{cells.join(" | ")} |" end def cast_item(item) diff --git a/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb index 72abfd584..b3f1222d2 100644 --- a/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +++ b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb @@ -26,15 +26,10 @@ def ==(other) table_name == other.table_name && ordinal_position == other.ordinal_position end - alias eql? == + alias_method :eql?, :== def hash - TypeMetadata.hash ^ - __getobj__.hash ^ - is_identity.hash ^ - is_primary.hash ^ - table_name.hash ^ - ordinal_position.hash + [TypeMetadata, __getobj__, is_identity, is_primary, table_name, ordinal_position].hash end private diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index 90104d8ad..adddc5014 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -35,16 +35,16 @@ def eql?(other) self.class == other.class && value == other.value end - alias :== :eql? + alias_method :==, :eql? def self.from_msgpack_ext(string) - type, value = string.chomp!("msgpack_ext").split(',') + type, value = string.chomp!("msgpack_ext").split(",") Data.new(value, type.constantize) end def to_msgpack_ext - [type.class.to_s, value].join(',') + "msgpack_ext" + [type.class.to_s, value].join(",") + "msgpack_ext" end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index f16eb77d5..56e652135 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -9,12 +9,12 @@ def sqlserver_type "date" end - def serialize(value) + def serialize(_value) value = super return value unless value.acts_like?(:date) - date = super(value).to_formatted_s(:_sqlserver_dateformat) - Data.new date, self + date = super.to_formatted_s(:_sqlserver_dateformat) + Data.new(date, self) end def deserialize(value) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 9c05b3f8d..d1d8f739f 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -11,13 +11,12 @@ def sqlserver_type "datetime" end - def serialize(value) + def serialize(_value) value = super return value unless value.acts_like?(:time) datetime = "#{value.to_formatted_s(:_sqlserver_datetime)}.#{quote_fractional(value)}" - - Data.new datetime, self + Data.new(datetime, self) end def deserialize(value) @@ -37,7 +36,7 @@ def quoted(value) def fast_string_to_time(string) time = ActiveSupport::TimeZone["UTC"].strptime(string, fast_string_to_time_format) new_time(time.year, time.month, time.day, time.hour, - time.min, time.sec, Rational(time.nsec, 1_000)) + time.min, time.sec, Rational(time.nsec, 1_000)) rescue ArgumentError super end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index db1a1ba78..9ac526a41 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -20,7 +20,7 @@ def fast_string_to_time_format end def apply_seconds_precision(value) - value.change usec: 0 if value + value&.change(usec: 0) end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 3fdf44523..407a644d6 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -7,13 +7,12 @@ module Type class Time < ActiveRecord::Type::Time include TimeValueFractional2 - def serialize(value) + def serialize(_value) value = super return value unless value.acts_like?(:time) time = "#{value.to_formatted_s(:_sqlserver_time)}.#{quote_fractional(value)}" - - Data.new time, self + Data.new(time, self) end def deserialize(value) @@ -34,12 +33,11 @@ def quoted(value) private - def cast_value(value) + def cast_value(_value) value = super - return value unless value.is_a?(::Time) - value = value.change(year: 2000, month: 01, day: 01) + value = value.change(year: 2000, month: 0o1, day: 0o1) apply_seconds_precision(value) end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb b/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb index f5c8744ea..7deff427d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb @@ -57,7 +57,7 @@ module TimeValueFractional2 def seconds_precision(value) seconds = super - seconds > fractional_max ? fractional_scale_max : seconds + (seconds > fractional_max) ? fractional_scale_max : seconds end def fractional_property diff --git a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb index d469f0bfd..7bf2b25d1 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb @@ -7,8 +7,6 @@ module Type class Uuid < String ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x - alias_method :serialize, :deserialize - def type :uuid end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 002847919..baf237495 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -10,8 +10,8 @@ module Utils # Inspired from Rails PostgreSQL::Name adapter object in their own Utils. class Name UNQUOTED_SCANNER = /\]?\./ - QUOTED_SCANNER = /\A\[.*?\]\./ - QUOTED_CHECKER = /\A\[/ + QUOTED_SCANNER = /\A\[.*?\]\./ + QUOTED_CHECKER = /\A\[/ attr_reader :server, :database, :schema, :object attr_reader :raw_name @@ -38,7 +38,7 @@ def server_quoted end def fully_qualified_database_quoted - [server_quoted, database_quoted].compact.join('.') + [server_quoted, database_quoted].compact.join(".") end def fully_qualified? @@ -65,7 +65,7 @@ def to_s end def quoted - parts.map { |p| quote(p) if p }.join('.') + parts.map { |p| quote(p) if p }.join(".") end def quoted_raw @@ -103,32 +103,30 @@ def parse_raw_name @schema = @parts.first end rest = scanner.rest - rest = rest.start_with?(".") ? rest[1..-1] : rest[0..-1] + rest = rest.start_with?(".") ? rest[1..] : rest[0..] @object = unquote(rest) @parts << @object end def quote(part) - part =~ /\A\[.*\]\z/ ? part : "[#{part.to_s.gsub(']', ']]')}]" + /\A\[.*\]\z/.match?(part) ? part : "[#{part.to_s.gsub("]", "]]")}]" end def unquote(part) - if part && part.start_with?("[") + if part&.start_with?("[") part[1..-2] else part end end - def parts - @parts - end + attr_reader :parts end extend self def quote_string(s) - s.to_s.gsub(/\'/, "''") + s.to_s.gsub("'", "''") end def quote_string_single(s) @@ -144,7 +142,7 @@ def quoted_raw(name) end def unquote_string(s) - s.to_s.gsub(/\'\'/, "'") + s.to_s.gsub("''", "'") end def extract_identifiers(name) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 746db1a57..39d0927fb 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -36,16 +36,16 @@ module ConnectionAdapters register "sqlserver", "ActiveRecord::ConnectionAdapters::SQLServerAdapter", "active_record/connection_adapters/sqlserver_adapter" class SQLServerAdapter < AbstractAdapter - include SQLServer::Version, - SQLServer::Quoting, - SQLServer::DatabaseStatements, - SQLServer::Showplan, - SQLServer::SchemaStatements, - SQLServer::DatabaseLimits, - SQLServer::DatabaseTasks, - SQLServer::Savepoints + include SQLServer::Savepoints + include SQLServer::DatabaseTasks + include SQLServer::DatabaseLimits + include SQLServer::SchemaStatements + include SQLServer::Showplan + include SQLServer::DatabaseStatements + include SQLServer::Quoting + include SQLServer::Version - ADAPTER_NAME = "SQLServer".freeze + ADAPTER_NAME = "SQLServer" # Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql) DEFAULT_TIME_PRECISION = 7 @@ -67,12 +67,12 @@ def dbconsole(config, options = {}) sqlserver_config = config.configuration_hash args = [] - args += ["-d", "#{config.database}"] if config.database - args += ["-U", "#{sqlserver_config[:username]}"] if sqlserver_config[:username] - args += ["-P", "#{sqlserver_config[:password]}"] if sqlserver_config[:password] + args += ["-d", config.database.to_s] if config.database + args += ["-U", sqlserver_config[:username].to_s] if sqlserver_config[:username] + args += ["-P", sqlserver_config[:password].to_s] if sqlserver_config[:password] if sqlserver_config[:host] - host_arg = +"tcp:#{sqlserver_config[:host]}" + host_arg = "tcp:#{sqlserver_config[:host]}" host_arg << ",#{sqlserver_config[:port]}" if sqlserver_config[:port] args += ["-S", host_arg] end @@ -83,7 +83,7 @@ def dbconsole(config, options = {}) def new_client(config) TinyTds::Client.new(config) rescue TinyTds::Error => error - if error.message.match(/database .* does not exist/i) + if /database .* does not exist/i.match?(error.message) raise ActiveRecord::NoDatabaseError else raise @@ -244,7 +244,11 @@ def active? end def reconnect - @raw_connection&.close rescue nil + begin + @raw_connection&.close + rescue + nil + end @raw_connection = nil @spid = nil @collation = nil @@ -255,7 +259,11 @@ def reconnect def disconnect! super - @raw_connection&.close rescue nil + begin + @raw_connection&.close + rescue + nil + end @raw_connection = nil @spid = nil @collation = nil @@ -338,21 +346,21 @@ class << self protected def initialize_type_map(m) - m.register_type %r{.*}, SQLServer::Type::UnicodeString.new + m.register_type %r{.*}, SQLServer::Type::UnicodeString.new # Exact Numerics - register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger - m.alias_type "bigint", "bigint(8)" - register_class_with_limit m, "int(4)", SQLServer::Type::Integer - m.alias_type "integer", "int(4)" - m.alias_type "int", "int(4)" - register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger - m.alias_type "smallint", "smallint(2)" - register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger - m.alias_type "tinyint", "tinyint(1)" - m.register_type "bit", SQLServer::Type::Boolean.new - m.register_type %r{\Adecimal}i do |sql_type| - scale = extract_scale(sql_type) + register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger + m.alias_type "bigint", "bigint(8)" + register_class_with_limit m, "int(4)", SQLServer::Type::Integer + m.alias_type "integer", "int(4)" + m.alias_type "int", "int(4)" + register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger + m.alias_type "smallint", "smallint(2)" + register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger + m.alias_type "tinyint", "tinyint(1)" + m.register_type "bit", SQLServer::Type::Boolean.new + m.register_type %r{\Adecimal}i do |sql_type| + scale = extract_scale(sql_type) precision = extract_precision(sql_type) if scale == 0 SQLServer::Type::DecimalWithoutScale.new(precision: precision) @@ -360,17 +368,17 @@ def initialize_type_map(m) SQLServer::Type::Decimal.new(precision: precision, scale: scale) end end - m.alias_type %r{\Anumeric}i, "decimal" - m.register_type "money", SQLServer::Type::Money.new - m.register_type "smallmoney", SQLServer::Type::SmallMoney.new + m.alias_type %r{\Anumeric}i, "decimal" + m.register_type "money", SQLServer::Type::Money.new + m.register_type "smallmoney", SQLServer::Type::SmallMoney.new # Approximate Numerics - m.register_type "float", SQLServer::Type::Float.new - m.register_type "real", SQLServer::Type::Real.new + m.register_type "float", SQLServer::Type::Float.new + m.register_type "real", SQLServer::Type::Real.new # Date and Time - m.register_type "date", SQLServer::Type::Date.new - m.register_type %r{\Adatetime} do |sql_type| + m.register_type "date", SQLServer::Type::Date.new + m.register_type %r{\Adatetime} do |sql_type| precision = extract_precision(sql_type) if precision SQLServer::Type::DateTime2.new precision: precision @@ -382,34 +390,34 @@ def initialize_type_map(m) precision = extract_precision(sql_type) SQLServer::Type::DateTimeOffset.new precision: precision end - m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new - m.register_type %r{\Atime}i do |sql_type| + m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new + m.register_type %r{\Atime}i do |sql_type| precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION SQLServer::Type::Time.new precision: precision end # Character Strings - register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char - register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar - m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new - m.register_type "text", SQLServer::Type::Text.new + register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char + register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar + m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new + m.register_type "text", SQLServer::Type::Text.new # Unicode Character Strings - register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar - register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar - m.alias_type "string", "nvarchar(4000)" - m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new - m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new - m.register_type "ntext", SQLServer::Type::UnicodeText.new + register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar + register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar + m.alias_type "string", "nvarchar(4000)" + m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new + m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new + m.register_type "ntext", SQLServer::Type::UnicodeText.new # Binary Strings - register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary - register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary - m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new + register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary + register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary + m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new # Other Data Types - m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new - m.register_type "timestamp", SQLServer::Type::Timestamp.new + m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new + m.register_type "timestamp", SQLServer::Type::Timestamp.new end end @@ -464,10 +472,10 @@ def initialize_dateformatter [a, b, c].each { |f| f.upcase! if f == "y" } dateformat = "%#{a}-%#{b}-%#{c}" - ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat - ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat - ::Time::DATE_FORMATS[:_sqlserver_time] = "%H:%M:%S" - ::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S" + ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat + ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat + ::Time::DATE_FORMATS[:_sqlserver_time] = "%H:%M:%S" + ::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S" ::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time| time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}" } @@ -475,12 +483,12 @@ def initialize_dateformatter def version_year @version_year ||= begin - if sqlserver_version =~ /vNext/ + if /vNext/.match?(sqlserver_version) 2016 else /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i end - rescue StandardError + rescue 2016 end end diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index d9d2258de..86d106750 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -28,7 +28,7 @@ def is_utf8? end def case_sensitive? - collation && collation.match(/_CS/) + collation&.match(/_CS/) end def init_with(coder) @@ -55,15 +55,10 @@ def ==(other) table_name == other.table_name && ordinal_position == other.ordinal_position end - alias :eql? :== + alias_method :eql?, :== def hash - Column.hash ^ - super.hash ^ - is_identity?.hash ^ - is_primary?.hash ^ - table_name.hash ^ - ordinal_position.hash + [Column, super, is_identity?, is_primary?, table_name, ordinal_position].hash end private diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index bbae97e7c..8b18b89bd 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -28,7 +28,7 @@ def create(master_established = false) end establish_connection(configuration) rescue ActiveRecord::StatementInvalid => e - if /database .* already exists/i === e.message + if /database .* already exists/i.match?(e.message) raise DatabaseAlreadyExists else raise @@ -68,7 +68,7 @@ def structure_dump(filename, _extra_flags) "-D #{Shellwords.escape(configuration_hash[:database])}", "-U #{Shellwords.escape(configuration_hash[:username])}", "-P #{Shellwords.escape(configuration_hash[:password])}", - "-o #{Shellwords.escape(filename)}", + "-o #{Shellwords.escape(filename)}" ] table_args = connection.tables.map { |t| Shellwords.escape(t) } command.concat(table_args) @@ -79,8 +79,8 @@ def structure_dump(filename, _extra_flags) dump = File.read(filename) dump.gsub!(/^USE .*$\nGO\n/, "") # Strip db USE statements dump.gsub!(/^GO\n/, "") # Strip db GO statements - dump.gsub!(/nvarchar\(8000\)/, "nvarchar(4000)") # Fix nvarchar(8000) column defs - dump.gsub!(/nvarchar\(-1\)/, "nvarchar(max)") # Fix nvarchar(-1) column defs + dump.gsub!("nvarchar(8000)", "nvarchar(4000)") # Fix nvarchar(8000) column defs + dump.gsub!("nvarchar(-1)", "nvarchar(max)") # Fix nvarchar(-1) column defs dump.gsub!(/text\(\d+\)/, "text") # Fix text(16) column defs File.open(filename, "w") { |file| file.puts dump } end @@ -124,7 +124,7 @@ def local_database?(configuration) def configuration_host_ip(configuration) return nil unless configuration.host - Socket::getaddrinfo(configuration.host, "echo", Socket::AF_INET)[0][3] + Socket.getaddrinfo(configuration.host, "echo", Socket::AF_INET)[0][3] end def local_ipaddr?(host_ip) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index a9e8609ba..68c7cb3ee 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -16,7 +16,9 @@ class SQLServer < Arel::Visitors::ToSql BIND_BLOCK = proc { |i| "@#{i - 1}" } private_constant :BIND_BLOCK - def bind_block; BIND_BLOCK; end + def bind_block + BIND_BLOCK + end def visit_Arel_Nodes_Bin(o, collector) visit o.expr, collector @@ -86,7 +88,6 @@ def prepare_update_statement(o) end end - def visit_Arel_Nodes_DeleteStatement(o, collector) if has_join_and_composite_primary_key?(o) delete_statement_using_join(o, collector) @@ -110,7 +111,7 @@ def delete_statement_using_join(o, collector) end def visit_Arel_Nodes_Lock(o, collector) - o.expr = Arel.sql("WITH(UPDLOCK)") if o.expr.to_s =~ /FOR UPDATE/ + o.expr = Arel.sql("WITH(UPDLOCK)") if /FOR UPDATE/.match?(o.expr.to_s) collector << " " visit o.expr, collector end @@ -124,12 +125,11 @@ def visit_Arel_Nodes_Offset(o, collector) def visit_Arel_Nodes_Limit(o, collector) if node_value(o) == 0 collector << FETCH0 - collector << ROWS_ONLY else collector << FETCH visit o.expr, collector - collector << ROWS_ONLY end + collector << ROWS_ONLY end def visit_Arel_Nodes_Grouping(o, collector) @@ -142,10 +142,10 @@ def visit_Arel_Nodes_HomogeneousIn(o, collector) visit o.left, collector - if o.type == :in - collector << " IN (" + collector << if o.type == :in + " IN (" else - collector << " NOT IN (" + " NOT IN (" end values = o.casted_values @@ -207,14 +207,14 @@ def visit_Arel_Table(o, collector) quote_table_name(o.name) end end - rescue Exception + rescue quote_table_name(o.name) end - if o.table_alias - collector << "#{table_name} #{quote_table_name o.table_alias}" + collector << if o.table_alias + "#{table_name} #{quote_table_name o.table_alias}" else - collector << table_name + table_name end end @@ -334,7 +334,7 @@ def node_value(node) end def select_statement_lock? - @select_statement && @select_statement.lock + @select_statement&.lock end def make_Fetch_Possible_And_Deterministic(o) @@ -367,7 +367,7 @@ def table_From_Statement(o) elsif Arel::Nodes::SqlLiteral === core.from Arel::Table.new(core.from) elsif Arel::Nodes::JoinSource === core.source - Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left.left + (Arel::Nodes::SqlLiteral === core.source.left) ? Arel::Table.new(core.source.left, @engine) : core.source.left.left end end diff --git a/test/cases/active_schema_test_sqlserver.rb b/test/cases/active_schema_test_sqlserver.rb index 078f36966..dcc87c9d3 100644 --- a/test/cases/active_schema_test_sqlserver.rb +++ b/test/cases/active_schema_test_sqlserver.rb @@ -12,48 +12,50 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase end after do - connection.drop_table :schema_test_table rescue nil + connection.drop_table :schema_test_table + rescue + nil end - it 'default index' do - assert_queries_match('CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do + it "default index" do + assert_queries_match("CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do connection.add_index :schema_test_table, "foo" end end - it 'unique index' do - assert_queries_match('CREATE UNIQUE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do + it "unique index" do + assert_queries_match("CREATE UNIQUE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do connection.add_index :schema_test_table, "foo", unique: true end end - it 'where condition on index' do + it "where condition on index" do assert_queries_match("CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo]) WHERE state = 'active'") do connection.add_index :schema_test_table, "foo", where: "state = 'active'" end end - it 'if index does not exist' do + it "if index does not exist" do assert_queries_match("IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = 'index_schema_test_table_on_foo') " \ "CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do connection.add_index :schema_test_table, "foo", if_not_exists: true end end - it 'clustered index' do - assert_queries_match('CREATE CLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do + it "clustered index" do + assert_queries_match("CREATE CLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do connection.add_index :schema_test_table, "foo", type: :clustered end end - it 'nonclustered index' do - assert_queries_match('CREATE NONCLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do + it "nonclustered index" do + assert_queries_match("CREATE NONCLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do connection.add_index :schema_test_table, "foo", type: :nonclustered end end end - describe 'collation' do + describe "collation" do it "create column with NOT NULL and COLLATE" do assert_nothing_raised do connection.create_table :not_null_with_collation_table, force: true, id: false do |t| @@ -61,12 +63,16 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase end end ensure - connection.drop_table :not_null_with_collation_table rescue nil + begin + connection.drop_table :not_null_with_collation_table + rescue + nil + end end end - describe 'datetimeoffset precision' do - it 'valid precisions are correct' do + describe "datetimeoffset precision" do + it "valid precisions are correct" do assert_nothing_raised do connection.create_table :datetimeoffset_precisions do |t| t.datetimeoffset :precision_default @@ -81,22 +87,30 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase assert_equal columns.find { |column| column.name == "precision_5" }.precision, 5 assert_equal columns.find { |column| column.name == "precision_7" }.precision, 7 ensure - connection.drop_table :datetimeoffset_precisions rescue nil + begin + connection.drop_table :datetimeoffset_precisions + rescue + nil + end end - it 'invalid precision raises exception' do + it "invalid precision raises exception" do assert_raise(ActiveRecord::ActiveRecordError) do connection.create_table :datetimeoffset_precisions do |t| t.datetimeoffset :precision_8, precision: 8 end end ensure - connection.drop_table :datetimeoffset_precisions rescue nil + begin + connection.drop_table :datetimeoffset_precisions + rescue + nil + end end end - describe 'time precision' do - it 'valid precisions are correct' do + describe "time precision" do + it "valid precisions are correct" do assert_nothing_raised do connection.create_table :time_precisions do |t| t.time :precision_default @@ -111,17 +125,25 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase assert_equal columns.find { |column| column.name == "precision_5" }.precision, 5 assert_equal columns.find { |column| column.name == "precision_7" }.precision, 7 ensure - connection.drop_table :time_precisions rescue nil + begin + connection.drop_table :time_precisions + rescue + nil + end end - it 'invalid precision raises exception' do + it "invalid precision raises exception" do assert_raise(ActiveRecord::ActiveRecordError) do connection.create_table :time_precisions do |t| t.time :precision_8, precision: 8 end end ensure - connection.drop_table :time_precisions rescue nil + begin + connection.drop_table :time_precisions + rescue + nil + end end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 96cc70bf0..9eb781185 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -41,21 +41,19 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "table exists works if table name prefixed by schema and owner" do - begin - assert_equal "topics", Topic.table_name - assert Topic.table_exists? + assert_equal "topics", Topic.table_name + assert Topic.table_exists? - # Test when owner included in table name. - Topic.table_name = "dbo.topics" - assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists." + # Test when owner included in table name. + Topic.table_name = "dbo.topics" + 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" - assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists." - ensure - Topic.table_name = "topics" - end + # 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" + assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists." + ensure + Topic.table_name = "topics" end it "test table existence across database schemas" do @@ -69,18 +67,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_not_equal arunit_database, arunit2_database # Assert that the Topics table exists when using the Topics connection. - assert arunit_connection.table_exists?('topics'), 'Topics table exists using table name' - assert arunit_connection.table_exists?('dbo.topics'), 'Topics table exists using owner and table name' - assert arunit_connection.table_exists?("#{arunit_database}.dbo.topics"), 'Topics table exists using database, owner and table name' + assert arunit_connection.table_exists?("topics"), "Topics table exists using table name" + assert arunit_connection.table_exists?("dbo.topics"), "Topics table exists using owner and table name" + assert arunit_connection.table_exists?("#{arunit_database}.dbo.topics"), "Topics table exists using database, owner and table name" # Assert that the Colleges table exists when using the Colleges connection. - assert arunit2_connection.table_exists?('colleges'), 'College table exists using table name' - assert arunit2_connection.table_exists?('dbo.colleges'), 'College table exists using owner and table name' - assert arunit2_connection.table_exists?("#{arunit2_database}.dbo.colleges"), 'College table exists using database, owner and table name' + assert arunit2_connection.table_exists?("colleges"), "College table exists using table name" + assert arunit2_connection.table_exists?("dbo.colleges"), "College table exists using owner and table name" + assert arunit2_connection.table_exists?("#{arunit2_database}.dbo.colleges"), "College table exists using database, owner and table name" # Assert that the tables exist when using each others connection. - assert arunit_connection.table_exists?("#{arunit2_database}.dbo.colleges"), 'Colleges table exists using Topics connection' - assert arunit2_connection.table_exists?("#{arunit_database}.dbo.topics"), 'Topics table exists using Colleges connection' + assert arunit_connection.table_exists?("#{arunit2_database}.dbo.colleges"), "Colleges table exists using Topics connection" + assert arunit2_connection.table_exists?("#{arunit_database}.dbo.topics"), "Topics table exists using Colleges connection" end it "return true to insert sql query for inserts only" do @@ -109,20 +107,20 @@ class AdapterTestSQLServer < ActiveRecord::TestCase db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") configuration = db_config.configuration_hash.merge(database: "nonexistent_activerecord_unittest") assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(configuration), - "expected database #{configuration[:database]} to not exist" + "expected database #{configuration[:database]} to not exist" end it "test database exists returns true when the database exists" do db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(db_config.configuration_hash), - "expected database #{db_config.database} to exist" + "expected database #{db_config.database} to exist" end it "test primary key violation" do - Post.create!(id: 0, title: 'Setup', body: 'Create post with primary key of zero') + Post.create!(id: 0, title: "Setup", body: "Create post with primary key of zero") assert_raise ActiveRecord::RecordNotUnique do - Post.create!(id: 0, title: 'Test', body: 'Try to create another post with primary key of zero') + Post.create!(id: 0, title: "Test", body: "Try to create another post with primary key of zero") end end @@ -132,12 +130,20 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end after do - connection.execute("SET LANGUAGE #{@default_language}") rescue nil + begin + connection.execute("SET LANGUAGE #{@default_language}") + rescue + nil + end connection.send :initialize_dateformatter end it "memos users dateformat" do - connection.execute("SET LANGUAGE us_english") rescue nil + begin + connection.execute("SET LANGUAGE us_english") + rescue + nil + end dateformat = connection.instance_variable_get(:@database_dateformat) assert_equal "mdy", dateformat end @@ -203,12 +209,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do - assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql) - assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted) - assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered) - assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_sp) - assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted_sp) - assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered_sp) + assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql) + assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted) + assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered) + assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_sp) + assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted_sp) + assert_equal "[funny_jokes]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered_sp) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted) @@ -276,7 +282,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe "disabling referential integrity" do before do - connection.disable_referential_integrity { SSTestHasPk.delete_all; SSTestHasFk.delete_all } + connection.disable_referential_integrity { + SSTestHasPk.delete_all + SSTestHasFk.delete_all + } @parent = SSTestHasPk.create! @member = SSTestHasFk.create!(fk_id: @parent.id) end @@ -417,8 +426,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal columns.size, SSTestCustomersView.columns.size columns.each do |colname| assert_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Column, - SSTestCustomersView.columns_hash[colname], - "Column name #{colname.inspect} was not found in these columns #{SSTestCustomersView.columns.map(&:name).inspect}" + SSTestCustomersView.columns_hash[colname], + "Column name #{colname.inspect} was not found in these columns #{SSTestCustomersView.columns.map(&:name).inspect}" end end @@ -444,8 +453,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal columns.size, SSTestStringDefaultsView.columns.size columns.each do |colname| assert_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Column, - SSTestStringDefaultsView.columns_hash[colname], - "Column name #{colname.inspect} was not found in these columns #{SSTestStringDefaultsView.columns.map(&:name).inspect}" + SSTestStringDefaultsView.columns_hash[colname], + "Column name #{colname.inspect} was not found in these columns #{SSTestStringDefaultsView.columns.map(&:name).inspect}" end end @@ -457,7 +466,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "find default values" do assert_equal "null", SSTestStringDefaultsView.new.pretend_null, - SSTestStringDefaultsView.columns_hash["pretend_null"].inspect + SSTestStringDefaultsView.columns_hash["pretend_null"].inspect end it "respond true to data_source_exists?" do @@ -467,12 +476,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase # That have more than 4000 chars for their definition it "cope with null returned for the definition" do - assert_nothing_raised() { SSTestStringDefaultsBigView.columns } + assert_nothing_raised { SSTestStringDefaultsBigView.columns } end it "using alternate view definition still be able to find real default" do assert_equal "null", SSTestStringDefaultsBigView.new.pretend_null, - SSTestStringDefaultsBigView.columns_hash["pretend_null"].inspect + SSTestStringDefaultsBigView.columns_hash["pretend_null"].inspect end end @@ -547,30 +556,30 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end - describe 'table is in non-dbo schema' do + describe "table is in non-dbo schema" do it "records can be created successfully" do assert_difference("Alien.count", 1) do - Alien.create!(name: 'Trisolarans') + Alien.create!(name: "Trisolarans") end end - it 'records can be inserted using SQL' do + it "records can be inserted using SQL" do assert_difference("Alien.count", 2) do Alien.lease_connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')") end end end - describe 'table names contains spaces' do - it 'records can be created successfully' do + describe "table names contains spaces" do + it "records can be created successfully" do assert_difference("TableWithSpaces.count", 1) do - TableWithSpaces.create!(name: 'Bob') + TableWithSpaces.create!(name: "Bob") end end end describe "exec_insert" do - it 'values clause should be case-insensitive' do + it "values clause should be case-insensitive" do assert_difference("Post.count", 4) do first_insert = connection.exec_insert("INSERT INTO [posts] ([id],[title],[body]) VALUES(100, 'Title', 'Body'), (102, 'Title', 'Body')") second_insert = connection.exec_insert("INSERT INTO [posts] ([id],[title],[body]) values(113, 'Body', 'Body'), (114, 'Body', 'Body')") @@ -586,33 +595,37 @@ def setup @conn = ActiveRecord::Base.lease_connection end - it 'raises an error when the foreign key is mismatched' do - error = assert_raises(ActiveRecord::MismatchedForeignKey) do + it "raises an error when the foreign key is mismatched" do + error = assert_raises(ActiveRecord::MismatchedForeignKey) do @conn.add_reference :engines, :old_car @conn.add_foreign_key :engines, :old_cars end assert_match( - %r/Column 'old_cars\.id' is not the same data type as referencing column 'engines\.old_car_id' in foreign key '.*'/, + %r{Column 'old_cars\.id' is not the same data type as referencing column 'engines\.old_car_id' in foreign key '.*'}, error.message ) assert_not_nil error.cause assert_equal @conn.pool, error.connection_pool ensure - @conn.execute("ALTER TABLE engines DROP COLUMN old_car_id") rescue nil + begin + @conn.execute("ALTER TABLE engines DROP COLUMN old_car_id") + rescue + nil + end end end describe "placeholder conditions" do - it 'using time placeholder' do + it "using time placeholder" do assert_equal Task.where("starting < ?", Time.now).count, 1 end - it 'using date placeholder' do + it "using date placeholder" do assert_equal Task.where("starting < ?", Date.today).count, 1 end - it 'using date-time placeholder' do + it "using date-time placeholder" do assert_equal Task.where("starting < ?", DateTime.current).count, 1 end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 517775446..cd7a9d499 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2,7 +2,22 @@ require "cases/helper_sqlserver" +require "models/author" +require "models/book" +require "models/car" +require "models/citation" +require "models/comment" +require "models/computer" +require "models/customer" +require "models/dashboard" +require "models/developer" require "models/event" +require "models/non_primary_key" +require "models/post" +require "models/tag" +require "models/task" +require "models/topic" + class UniquenessValidationTest < ActiveRecord::TestCase # So sp_executesql swallows this exception. Run without prepared to see it. coerce_tests! :test_validate_uniqueness_with_limit @@ -62,7 +77,6 @@ def test_partial_index_coerced end end -require "models/event" module ActiveRecord class AdapterTest < ActiveRecord::TestCase # Legacy binds are not supported. @@ -174,21 +188,20 @@ def test_truncate_tables_with_query_cache end end -require "models/topic" class AttributeMethodsTest < ActiveRecord::TestCase # Use IFF for boolean statement in SELECT coerce_tests! %r{typecast attribute from select to false} def test_typecast_attribute_from_select_to_false_coerced - Topic.create(:title => "Budget") - topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 2, 1, 0) as is_test").first + Topic.create(title: "Budget") + topic = Topic.all.merge!(select: "topics.*, IIF (1 = 2, 1, 0) as is_test").first assert_not_predicate topic, :is_test? end # Use IFF for boolean statement in SELECT coerce_tests! %r{typecast attribute from select to true} def test_typecast_attribute_from_select_to_true_coerced - Topic.create(:title => "Budget") - topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first + Topic.create(title: "Budget") + topic = Topic.all.merge!(select: "topics.*, IIF (1 = 1, 1, 0) as is_test").first assert_predicate topic, :is_test? end end @@ -260,9 +273,9 @@ 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) + sub = Arel::Nodes::BindParam.new(1) binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)] - sql = "select * from topics where id = #{sub.to_sql}" + sql = "select * from topics where id = #{sub.to_sql}" @connection.exec_query(sql, "SQL", binds) @@ -347,7 +360,7 @@ def test_payload_row_count_on_select_all_coerced original_test_payload_row_count_on_select_all ensure - Book.where(author_id: nil, name: 'row count book 1').delete_all + Book.where(author_id: nil, name: "row count book 1").delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end @@ -358,7 +371,7 @@ def test_payload_row_count_on_pluck_coerced original_test_payload_row_count_on_pluck ensure - Book.where(author_id: nil, name: 'row count book 2').delete_all + Book.where(author_id: nil, name: "row count book 2").delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end @@ -369,7 +382,7 @@ def test_payload_row_count_on_raw_sql_coerced original_test_payload_row_count_on_raw_sql ensure - Book.where(author_id: nil, name: 'row count book 3').delete_all + Book.where(author_id: nil, name: "row count book 3").delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end @@ -388,7 +401,7 @@ class CalculationsTest < ActiveRecord::TestCase 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 } + expected = {1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1} assert_not_predicate accounts, :loaded? assert_equal expected, accounts.count @@ -448,11 +461,11 @@ def test_select_avg_with_group_by_as_virtual_attribute_with_ar_coerced rails_core = companies(:rails_core) account = Account - .select(:firm_id, "AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit") - .where(firm: rails_core) - .group(:firm_id) - .order(:firm_id) - .take! + .select(:firm_id, "AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit") + .where(firm: rails_core) + .group(:firm_id) + .order(:firm_id) + .take! # id was not selected, so it should be nil # (cannot select id because it wasn't used in the GROUP BY clause) @@ -492,7 +505,6 @@ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql_coerce assert_equal(52.5, firm.avg_credit_limit) end - # In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging. # SELECT columns must be in the GROUP clause. coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar @@ -500,11 +512,11 @@ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced rails_core = companies(:rails_core) firm = DependentFirm - .select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit") - .where(id: rails_core) - .joins(:account) - .group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description, :status) - .take! + .select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit") + .where(id: rails_core) + .joins(:account) + .group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description, :status) + .take! # all the DependentFirm attributes should be present assert_equal rails_core, firm @@ -640,7 +652,7 @@ class ColumnsTest < ActiveRecord::TestCase # Our defaults are real 70000 integers vs '70000' strings. coerce_tests! :test_rename_column_preserves_default_value_not_null def test_rename_column_preserves_default_value_not_null_coerced - add_column "test_models", "salary", :integer, :default => 70000 + add_column "test_models", "salary", :integer, default: 70000 default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default assert_equal 70000, default_before rename_column "test_models", "salary", "annual_salary" @@ -654,8 +666,8 @@ def test_rename_column_preserves_default_value_not_null_coerced coerce_tests! :test_remove_column_with_multi_column_index def test_remove_column_with_multi_column_index_coerced add_column "test_models", :hat_size, :integer - add_column "test_models", :hat_style, :string, :limit => 100 - add_index "test_models", ["hat_style", "hat_size"], :unique => true + add_column "test_models", :hat_style, :string, limit: 100 + add_index "test_models", ["hat_style", "hat_size"], unique: true assert_equal 1, connection.indexes("test_models").size remove_column("test_models", "hat_size") assert_equal [], connection.indexes("test_models").map(&:name) @@ -682,14 +694,20 @@ class MigrationTest < ActiveRecord::TestCase coerce_tests! :test_add_column_with_casted_type_if_not_exists_set_to_true def test_add_column_with_casted_type_if_not_exists_set_to_true_coerced migration_a = Class.new(ActiveRecord::Migration::Current) { - def version; 100 end + def version + 100 + end + def migrate(x) add_column "people", "last_name", :binary end }.new migration_b = Class.new(ActiveRecord::Migration::Current) { - def version; 101 end + def version + 101 + end + def migrate(x) add_column "people", "last_name", :binary, if_not_exists: true end @@ -718,7 +736,10 @@ def test_create_table_on_7_0_coerced long_table_name = "a" * (connection.table_name_length + 1) migration = Class.new(ActiveRecord::Migration[7.0]) { @@long_table_name = long_table_name - def version; 100 end + def version + 100 + end + def migrate(x) create_table @@long_table_name end @@ -729,7 +750,11 @@ def migrate(x) end assert_match(/The identifier that starts with '#{long_table_name[0...-1]}' is too long/i, error.message) ensure - connection.drop_table(long_table_name) rescue nil + begin + connection.drop_table(long_table_name) + rescue + nil + end end # SQL Server truncates long table names when renaming (https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-rename-transact-sql?view=sql-server-ver16). @@ -740,7 +765,10 @@ def test_rename_table_on_7_0_coerced migration = Class.new(ActiveRecord::Migration[7.0]) { @@long_table_name = long_table_name - def version; 100 end + def version + 100 + end + def migrate(x) rename_table :more_testings, @@long_table_name end @@ -751,14 +779,22 @@ def migrate(x) assert_not connection.table_exists?(:more_testings) assert connection.table_exists?(long_table_name[0...-1]) ensure - connection.drop_table(:more_testings) rescue nil - connection.drop_table(long_table_name[0...-1]) rescue nil + begin + connection.drop_table(:more_testings) + rescue + nil + end + begin + connection.drop_table(long_table_name[0...-1]) + rescue + nil + end end # SQL Server has a different maximum index name length. coerce_tests! :test_add_index_errors_on_too_long_name_7_0 def test_add_index_errors_on_too_long_name_7_0_coerced - long_index_name = 'a' * (connection.index_name_length + 1) + long_index_name = "a" * (connection.index_name_length + 1) migration = Class.new(ActiveRecord::Migration[7.0]) { @@long_index_name = long_index_name @@ -771,13 +807,13 @@ def migrate(x) error = assert_raises(StandardError) do ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate end - assert_match(/Index name \'#{long_index_name}\' on table \'testings\' is too long/i, error.message) + assert_match(/Index name '#{long_index_name}' on table 'testings' is too long/i, error.message) end # SQL Server has a different maximum index name length. coerce_tests! :test_create_table_add_index_errors_on_too_long_name_7_0 def test_create_table_add_index_errors_on_too_long_name_7_0_coerced - long_index_name = 'a' * (connection.index_name_length + 1) + long_index_name = "a" * (connection.index_name_length + 1) migration = Class.new(ActiveRecord::Migration[7.0]) { @@long_index_name = long_index_name @@ -794,9 +830,13 @@ def migrate(x) error = assert_raises(StandardError) do ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate end - assert_match(/Index name \'#{long_index_name}\' on table \'more_testings\' is too long/i, error.message) + assert_match(/Index name '#{long_index_name}' on table 'more_testings' is too long/i, error.message) ensure - connection.drop_table :more_testings rescue nil + begin + connection.drop_table :more_testings + rescue + nil + end end end end @@ -829,19 +869,26 @@ module DatabaseTasksSetupper def setup @sqlserver_tasks = Class.new do - def create; end + def create + end - def drop; end + def drop + end - def purge; end + def purge + end - def charset; end + def charset + end - def collation; end + def collation + end - def structure_dump(*); end + def structure_dump(*) + end - def structure_load(*); end + def structure_load(*) + end end.new $stdout, @original_stdout = StringIO.new, $stdout @@ -862,7 +909,7 @@ class DatabaseTasksCreateTest < ActiveRecord::TestCase def test_sqlserver_create with_stubbed_new do - assert_called(eval("@sqlserver_tasks"), :create) do + assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :create) do ActiveRecord::Tasks::DatabaseTasks.create "adapter" => :sqlserver end end @@ -875,7 +922,7 @@ class DatabaseTasksDropTest < ActiveRecord::TestCase def test_sqlserver_drop with_stubbed_new do - assert_called(eval("@sqlserver_tasks"), :drop) do + assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :drop) do ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => :sqlserver end end @@ -888,7 +935,7 @@ class DatabaseTasksPurgeTest < ActiveRecord::TestCase def test_sqlserver_purge with_stubbed_new do - assert_called(eval("@sqlserver_tasks"), :purge) do + assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :purge) do ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => :sqlserver end end @@ -901,7 +948,7 @@ class DatabaseTasksCharsetTest < ActiveRecord::TestCase def test_sqlserver_charset with_stubbed_new do - assert_called(eval("@sqlserver_tasks"), :charset) do + assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :charset) do ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => :sqlserver end end @@ -914,7 +961,7 @@ class DatabaseTasksCollationTest < ActiveRecord::TestCase def test_sqlserver_collation with_stubbed_new do - assert_called(eval("@sqlserver_tasks"), :collation) do + assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :collation) do ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => :sqlserver end end @@ -928,10 +975,10 @@ class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase def test_sqlserver_structure_dump with_stubbed_new do assert_called_with( - eval("@sqlserver_tasks"), :structure_dump, + eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :structure_dump, ["awesome-file.sql", nil] ) do - ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :sqlserver }, "awesome-file.sql") + ActiveRecord::Tasks::DatabaseTasks.structure_dump({"adapter" => :sqlserver}, "awesome-file.sql") end end end @@ -944,11 +991,11 @@ class DatabaseTasksStructureLoadTest < ActiveRecord::TestCase def test_sqlserver_structure_load with_stubbed_new do assert_called_with( - eval("@sqlserver_tasks"), + eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :structure_load, ["awesome-file.sql", nil] ) do - ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => :sqlserver }, "awesome-file.sql") + ActiveRecord::Tasks::DatabaseTasks.structure_load({"adapter" => :sqlserver}, "awesome-file.sql") end end end @@ -981,15 +1028,12 @@ def test_count_with_include_coerced coerce_tests! %r{including association based on sql condition and no database column} end -require "models/topic" -require "models/customer" -require "models/non_primary_key" class FinderTest < ActiveRecord::TestCase fixtures :customers, :topics, :authors # We have implicit ordering, via FETCH. coerce_tests! %r{doesn't have implicit ordering}, - :test_find_doesnt_have_implicit_ordering + :test_find_doesnt_have_implicit_ordering # Assert SQL Server limit implementation coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit @@ -1257,8 +1301,6 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase coerce_tests! :test_does_not_override_select end -require "models/developer" -require "models/computer" class NestedRelationScopingTest < ActiveRecord::TestCase # Assert SQL Server limit implementation coerce_tests! :test_merge_options @@ -1274,7 +1316,6 @@ def test_merge_options_coerced end end -require "models/topic" class PersistenceTest < ActiveRecord::TestCase # Rails test required updating a identity column. coerce_tests! :test_update_columns_changing_id @@ -1301,7 +1342,6 @@ def test_update_coerced coerce_tests! :test_model_with_no_auto_populated_fields_still_returns_primary_key_after_insert end -require "models/author" class UpdateAllTest < ActiveRecord::TestCase # Regular expression slightly different. coerce_tests! :test_update_all_doesnt_ignore_order @@ -1334,7 +1374,7 @@ def test_update_all_with_group_by_coerced assert_operator posts.length, :>, 0 assert posts.all? { |post| post.comments.length >= minimum_comments_count } - assert posts.all? { |post| "ig" == post.title } + assert posts.all? { |post| post.title == "ig" } post = Post.select(:id, :title).group(:title).joins(:comments).group("posts.id").having("count(comments.id) < #{minimum_comments_count}").first assert_not_equal "ig", post.title @@ -1359,7 +1399,6 @@ def test_delete_all_with_group_by_and_having_coerced end end -require "models/topic" module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase # Same as original test except string has `N` prefix to indicate unicode string. @@ -1371,7 +1410,7 @@ def test_registering_new_handlers_coerced # Same as original test except string has `N` prefix to indicate unicode string. coerce_tests! :test_registering_new_handlers_for_association def test_registering_new_handlers_for_association_coerced - assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: { title: /rails/ }).to_sql + assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: {title: /rails/}).to_sql end # Same as original test except string has `N` prefix to indicate unicode string. @@ -1390,7 +1429,6 @@ def topic_title end end -require "models/task" class QueryCacheTest < ActiveRecord::TestCase # SQL Server adapter not in list of supported adapters in original test. coerce_tests! :test_cache_does_not_wrap_results_in_arrays @@ -1401,7 +1439,6 @@ def test_cache_does_not_wrap_results_in_arrays_coerced end end -require "models/post" class RelationTest < ActiveRecord::TestCase # Use LEN() instead of LENGTH() function. coerce_tests! :test_reverse_order_with_function @@ -1429,7 +1466,7 @@ def test_reorder_with_take_coerced assert Post.order(:title).reorder(nil).take end assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}" - assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}" + assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}" end # We have implicit ordering, via FETCH. @@ -1441,7 +1478,7 @@ def test_reorder_with_first_coerced end assert_equal posts(:welcome), post assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}" - assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}" + assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}" end # We are not doing order duplicate removal anymore. @@ -1455,7 +1492,7 @@ def test_reorder_with_first_coerced def test_multiple_where_and_having_clauses_coerced post = Post.first having_then_where = Post.having(id: post.id).where(title: post.title) - .having(id: post.id).where(title: post.title).group(:id).select(:id) + .having(id: post.id).where(title: post.title).group(:id).select(:id) assert_equal [post], having_then_where end @@ -1545,7 +1582,6 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced end end -require "models/post" class SanitizeTest < ActiveRecord::TestCase # Use nvarchar string (N'') in assert coerce_tests! :test_sanitize_sql_like_example_use_case @@ -1581,17 +1617,17 @@ class SchemaDumperTest < ActiveRecord::TestCase # Use nvarchar string (N'') in assert coerce_tests! :test_dump_schema_versions_outputs_lexically_reverse_ordered_versions_regardless_of_database_order def test_dump_schema_versions_outputs_lexically_reverse_ordered_versions_regardless_of_database_order_coerced - versions = %w{ 20100101010101 20100201010101 20100301010101 } + versions = %w[20100101010101 20100201010101 20100301010101] versions.shuffle.each do |v| @schema_migration.create_version(v) end schema_info = ActiveRecord::Base.lease_connection.dump_schema_versions expected = <<~STR - INSERT INTO #{quote_table_name("schema_migrations")} (version) VALUES - (N'20100301010101'), - (N'20100201010101'), - (N'20100101010101'); + INSERT INTO #{quote_table_name("schema_migrations")} (version) VALUES + (N'20100301010101'), + (N'20100201010101'), + (N'20100101010101'); STR assert_equal expected.strip, schema_info ensure @@ -1611,7 +1647,7 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced # Fall through false positive with no filter. coerce_tests! :test_schema_dumps_partial_indices def test_schema_dumps_partial_indices_coerced - index_definition = standard_dump.split(/\n/).grep(/t.index.*company_partial_index/).first.strip + index_definition = standard_dump.split("\n").grep(/t.index.*company_partial_index/).first.strip assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", where: "([rating]>(10))"', index_definition end @@ -1628,7 +1664,7 @@ def test_schema_dump_includes_decimal_options_coerced # SQL Server formats the check constraint expression differently. coerce_tests! :test_schema_dumps_check_constraints def test_schema_dumps_check_constraints_coerced - constraint_definition = dump_table_schema("products").split(/\n/).grep(/t.check_constraint.*products_price_check/).first.strip + constraint_definition = dump_table_schema("products").split("\n").grep(/t.check_constraint.*products_price_check/).first.strip assert_equal 't.check_constraint "[price]>[discounted_price]", name: "products_price_check"', constraint_definition end end @@ -1648,14 +1684,14 @@ class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase setup do @connection = ActiveRecord::Base.lease_connection @connection.create_table :dump_defaults, force: true do |t| - t.string :string_with_default, default: "Hello!" - t.date :date_with_default, default: "2014-06-05" + t.string :string_with_default, default: "Hello!" + t.date :date_with_default, default: "2014-06-05" t.datetime :datetime_with_default, default: "2014-06-05 07:17:04" - t.time :time_with_default, default: "07:17:04" - t.decimal :decimal_with_default, default: "1234567890.0123456789", precision: 20, scale: 10 + t.time :time_with_default, default: "07:17:04" + t.decimal :decimal_with_default, default: "1234567890.0123456789", precision: 20, scale: 10 - t.text :text_with_default, default: "John' Doe" - t.text :uuid, default: -> { "newid()" } + t.text :text_with_default, default: "John' Doe" + t.text :uuid, default: -> { "newid()" } end end @@ -1672,7 +1708,6 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase coerce_tests! %r{inspect on Model class does not raise} end -require "models/topic" class TransactionTest < ActiveRecord::TestCase # SQL Server does not have query for release_savepoint. coerce_tests! :test_releasing_named_savepoints @@ -1718,7 +1753,7 @@ def test_nested_transactions_after_disable_lazy_transactions_coerced /DELETE/i, /^SAVE TRANSACTION/i, /DELETE/i, - /COMMIT/i, + /COMMIT/i ] assert_equal expected_queries.size, actual_queries.size @@ -1754,7 +1789,7 @@ def test_nested_transactions_skip_excess_savepoints_coerced /^SAVE TRANSACTION/i, /DELETE/i, /DELETE/i, - /COMMIT/i, + /COMMIT/i ] assert_equal expected_queries.size, actual_queries.size @@ -1764,7 +1799,6 @@ def test_nested_transactions_skip_excess_savepoints_coerced end end -require "models/tag" class TransactionIsolationTest < ActiveRecord::TestCase # SQL Server will lock the table for counts even when both # connections are `READ COMMITTED`. So we bypass with `READPAST`. @@ -1784,7 +1818,6 @@ class TransactionIsolationTest < ActiveRecord::TestCase coerce_tests! %r{repeatable read} end -require "models/book" class ViewWithPrimaryKeyTest < ActiveRecord::TestCase # We have a few view tables. use includes vs equality. coerce_tests! :test_views @@ -1808,7 +1841,6 @@ def test_views_coerced end end -require "models/author" class YamlSerializationTest < ActiveRecord::TestCase coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced @@ -1857,7 +1889,7 @@ class TimePrecisionTest < ActiveRecord::TestCase coerce_tests! :test_time_precision_is_truncated_on_assignment def test_time_precision_is_truncated_on_assignment_coerced @connection.create_table(:foos, force: true) - @connection.add_column :foos, :start, :time, precision: 0 + @connection.add_column :foos, :start, :time, precision: 0 @connection.add_column :foos, :finish, :time, precision: 6 time = ::Time.now.change(nsec: 123456789) @@ -1893,8 +1925,8 @@ def test_default_positive_integer_coerced coerce_tests! :test_default_negative_integer def test_default_negative_integer_coerced record = DefaultNumber.new - assert_equal (-5), record.negative_integer - assert_equal (-5), record.negative_integer_before_type_cast + assert_equal(-5, record.negative_integer) + assert_equal(-5, record.negative_integer_before_type_cast) end # We do better with native types and do not return strings for everything. @@ -1923,7 +1955,6 @@ class CacheKeyTest < ActiveRecord::TestCase end end -require "models/book" module ActiveRecord class StatementCacheTest < ActiveRecord::TestCase # Getting random failures. @@ -1936,7 +1967,7 @@ def test_statement_cache_values_differ_coerced original_test_statement_cache_values_differ ensure - Book.where(author_id: nil, name: 'my book').delete_all + Book.where(author_id: nil, name: "my book").delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end end @@ -1946,7 +1977,7 @@ module ActiveRecord module ConnectionAdapters class SchemaCacheTest < ActiveRecord::TestCase # Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call. - coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ + coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"]) # Cast type in SQL Server is :varchar rather than Unicode :string. coerce_tests! :test_yaml_load_8_0_dump_without_cast_type_still_get_the_right_one @@ -1978,8 +2009,6 @@ def schema_dump_8_0_path end end -require "models/post" -require "models/comment" class UnsafeRawSqlTest < ActiveRecord::TestCase fixtures :posts @@ -2100,9 +2129,9 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "order: allows valid arguments with COLLATE" do collation_name = "Latin1_General_CS_AS_WS" - ids_expected = Post.order(Arel.sql(%Q'author_id, title COLLATE #{collation_name} DESC')).pluck(:id) + ids_expected = Post.order(Arel.sql(%(author_id, title COLLATE #{collation_name} DESC))).pluck(:id) - ids = Post.order(["author_id", %Q'title COLLATE #{collation_name} DESC']).pluck(:id) + ids = Post.order(["author_id", %(title COLLATE #{collation_name} DESC)]).pluck(:id) assert_equal ids_expected, ids end @@ -2176,14 +2205,13 @@ class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase end end -require "models/book" class EnumTest < ActiveRecord::TestCase # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. coerce_tests! %r{enums are distinct per class} test "enums are distinct per class coerced" do Book.lease_connection.remove_index(:books, column: [:author_id, :name]) - send(:'original_enums are distinct per class') + send(:"original_enums are distinct per class") ensure Book.where(author_id: nil, name: nil).delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) @@ -2194,7 +2222,7 @@ class EnumTest < ActiveRecord::TestCase test "creating new objects with enum scopes coerced" do Book.lease_connection.remove_index(:books, column: [:author_id, :name]) - send(:'original_creating new objects with enum scopes') + send(:"original_creating new objects with enum scopes") ensure Book.where(author_id: nil, name: nil).delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) @@ -2205,7 +2233,7 @@ class EnumTest < ActiveRecord::TestCase test "enums are inheritable coerced" do Book.lease_connection.remove_index(:books, column: [:author_id, :name]) - send(:'original_enums are inheritable') + send(:"original_enums are inheritable") ensure Book.where(author_id: nil, name: nil).delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) @@ -2216,14 +2244,13 @@ class EnumTest < ActiveRecord::TestCase test "serializable? with large number label coerced" do Book.lease_connection.remove_index(:books, column: [:author_id, :name]) - send(:'original_serializable\? with large number label') + send(:"original_serializable\\? with large number label") ensure Book.where(author_id: nil, name: nil).delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end end -require "models/citation" class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase fixtures :citations @@ -2258,7 +2285,7 @@ def test_verbose_query_logs_coerced class ReloadModelsTest < ActiveRecord::TestCase # Skip test on Windows. The number of arguments passed to `IO.popen` in # `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle. - coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ + coerce_tests! :test_has_one_with_reload if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"]) end class MarshalSerializationTest < ActiveRecord::TestCase @@ -2354,15 +2381,14 @@ def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_ class MigratorTest < ActiveRecord::TestCase # Test fails on Windows AppVeyor CI for unknown reason. - coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ + coerce_tests! :test_migrator_db_has_no_schema_migrations_table if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"]) end class MultiDbMigratorTest < ActiveRecord::TestCase # Test fails on Windows AppVeyor CI for unknown reason. - coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ + coerce_tests! :test_migrator_db_has_no_schema_migrations_table if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"]) end -require "models/book" class FieldOrderedValuesTest < ActiveRecord::TestCase # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. coerce_tests! :test_in_order_of_with_enums_values @@ -2409,7 +2435,6 @@ def test_in_order_of_with_nil_coerced end end -require "models/dashboard" class QueryLogsTest < ActiveRecord::TestCase # SQL requires double single-quotes. coerce_tests! :test_sql_commenter_format @@ -2429,7 +2454,7 @@ def test_sqlcommenter_format_value_coerced ActiveRecord::QueryLogs.tags = [ :application, - { tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } }, + {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 @@ -2444,7 +2469,7 @@ def test_sqlcommenter_format_value_string_coercible_coerced ActiveRecord::QueryLogs.tags = [ :application, - { custom_proc: -> { 1234 } }, + {custom_proc: -> { 1234 }} ] assert_queries_match(%r{custom_proc=''1234''\*/}) do @@ -2461,9 +2486,9 @@ def test_sqlcommenter_format_allows_string_keys_coerced :application, { "string" => "value", - tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", - custom_proc: -> { "Joe's Shack" } - }, + :tracestate => "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", + :custom_proc => -> { "Joe's Shack" } + } ] assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do @@ -2474,7 +2499,7 @@ def test_sqlcommenter_format_allows_string_keys_coerced # Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres. coerce_tests! :test_invalid_encoding_query def test_invalid_encoding_query_coerced - ActiveRecord::QueryLogs.tags = [ :application ] + ActiveRecord::QueryLogs.tags = [:application] assert_raises ActiveRecord::StatementInvalid do ActiveRecord::Base.lease_connection.execute "select 1 as '\xFF'" end @@ -2487,8 +2512,8 @@ class InsertAllTest < ActiveRecord::TestCase def test_insert_all_returns_requested_sql_fields_coerced skip unless supports_insert_returning? - result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name") - assert_equal %w[ REWORK ], result.pluck("name") + result = Book.insert_all! [{name: "Rework", author_id: 1}], returning: Arel.sql("UPPER(INSERTED.name) as name") + assert_equal %w[REWORK], result.pluck("name") end # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. @@ -2538,6 +2563,7 @@ def thread_encrypting_and_decrypting(thread_label) # can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects. class ActiveRecordMessagePackTest < ActiveRecord::TestCase private + undef_method :serializer def serializer @serializer ||= ::MessagePack::Factory.new.tap do |factory| @@ -2560,7 +2586,7 @@ class TestDatabasesTest < ActiveRecord::TestCase module ActiveRecord module ConnectionAdapters - class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase + class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase # Tests are not about a specific adapter. coerce_all_tests! end @@ -2684,7 +2710,6 @@ def resolve_raises_if_the_adapter_is_using_the_pre_7_2_adapter_registration_API end end - module ActiveRecord class TableMetadataTest < ActiveSupport::TestCase # Adapter returns an object that is subclass of what is expected in the original test. @@ -2716,7 +2741,6 @@ def type_for_attribute_is_not_aware_of_custom_types_coerced end end -require "models/car" class ExplainTest < ActiveRecord::TestCase # Expected query slightly different from because of 'sp_executesql' and query parameters. coerce_tests! :test_relation_explain_with_first @@ -2735,7 +2759,6 @@ def test_relation_explain_with_last_coerced expected_query = capture_sql { Car.all.last }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1] - expected_query = expected_query message = Car.all.explain.last assert_match(/^EXPLAIN/, message) @@ -2778,7 +2801,7 @@ def test_with_recursive_coerced relation = Company.with_recursive( top_companies_and_children: [ Company.where(firm_id: nil), - Company.joins("JOIN top_companies_and_children ON companies.firm_id = top_companies_and_children.id"), + Company.joins("JOIN top_companies_and_children ON companies.firm_id = top_companies_and_children.id") ] ).from("top_companies_and_children AS companies") diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 5da2ab4e4..e55937662 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -11,11 +11,13 @@ class ColumnTestSQLServer < ActiveRecord::TestCase describe "ActiveRecord::ConnectionAdapters::SQLServer::Type" do let(:obj) { SSTestDatatype.new } - Type = ActiveRecord::ConnectionAdapters::SQLServer::Type - - def new_obj; SSTestDatatype.new; end + def new_obj + SSTestDatatype.new + end - def column(name); SSTestDatatype.columns_hash[name]; end + def column(name) + SSTestDatatype.columns_hash[name] + end def assert_obj_set_and_save(attribute, value) obj.send :"#{attribute}=", value @@ -30,80 +32,80 @@ def assert_obj_set_and_save(attribute, value) it "int(4) PRIMARY KEY" do col = column("id") - _(col.sql_type).must_equal "int(4)" - _(col.null).must_equal false + _(col.sql_type).must_equal "int(4)" + _(col.null).must_equal false end it "bigint(8)" do col = column("bigint") - _(col.sql_type).must_equal "bigint(8)" - _(col.type).must_equal :integer - _(col.null).must_equal true - _(col.default).must_equal 42 - _(obj.bigint).must_equal 42 + _(col.sql_type).must_equal "bigint(8)" + _(col.type).must_equal :integer + _(col.null).must_equal true + _(col.default).must_equal 42 + _(obj.bigint).must_equal 42 _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::BigInteger - _(type.limit).must_equal 8 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::BigInteger + _(type.limit).must_equal 8 assert_obj_set_and_save :bigint, -9_223_372_036_854_775_808 assert_obj_set_and_save :bigint, 9_223_372_036_854_775_807 end it "int(4)" do col = column("int") - _(col.sql_type).must_equal "int(4)" - _(col.type).must_equal :integer - _(col.null).must_equal true - _(col.default).must_equal 42 - _(obj.int).must_equal 42 + _(col.sql_type).must_equal "int(4)" + _(col.type).must_equal :integer + _(col.null).must_equal true + _(col.default).must_equal 42 + _(obj.int).must_equal 42 _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Integer - _(type.limit).must_equal 4 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Integer + _(type.limit).must_equal 4 assert_obj_set_and_save :int, -2_147_483_648 assert_obj_set_and_save :int, 2_147_483_647 end it "smallint(2)" do col = column("smallint") - _(col.sql_type).must_equal "smallint(2)" - _(col.type).must_equal :integer - _(col.null).must_equal true - _(col.default).must_equal 42 - _(obj.smallint).must_equal 42 + _(col.sql_type).must_equal "smallint(2)" + _(col.type).must_equal :integer + _(col.null).must_equal true + _(col.default).must_equal 42 + _(obj.smallint).must_equal 42 _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::SmallInteger - _(type.limit).must_equal 2 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::SmallInteger + _(type.limit).must_equal 2 assert_obj_set_and_save :smallint, -32_768 assert_obj_set_and_save :smallint, 32_767 end it "tinyint(1)" do col = column("tinyint") - _(col.sql_type).must_equal "tinyint(1)" - _(col.type).must_equal :integer - _(col.null).must_equal true - _(col.default).must_equal 42 - _(obj.tinyint).must_equal 42 + _(col.sql_type).must_equal "tinyint(1)" + _(col.type).must_equal :integer + _(col.null).must_equal true + _(col.default).must_equal 42 + _(obj.tinyint).must_equal 42 _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::TinyInteger - _(type.limit).must_equal 1 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::TinyInteger + _(type.limit).must_equal 1 assert_obj_set_and_save :tinyint, 0 assert_obj_set_and_save :tinyint, 255 end it "bit" do col = column("bit") - _(col.sql_type).must_equal "bit" - _(col.type).must_equal :boolean - _(col.null).must_equal true - _(col.default).must_equal true - _(obj.bit).must_equal true + _(col.sql_type).must_equal "bit" + _(col.type).must_equal :boolean + _(col.null).must_equal true + _(col.default).must_equal true + _(obj.bit).must_equal true _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Boolean + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Boolean _(type.limit).must_be_nil obj.bit = 0 _(obj.bit).must_equal false @@ -117,17 +119,17 @@ def assert_obj_set_and_save(attribute, value) it "decimal(9,2)" do col = column("decimal_9_2") - _(col.sql_type).must_equal "decimal(9,2)" - _(col.type).must_equal :decimal - _(col.null).must_equal true - _(col.default).must_equal BigDecimal("12345.01") - _(obj.decimal_9_2).must_equal BigDecimal("12345.01") + _(col.sql_type).must_equal "decimal(9,2)" + _(col.type).must_equal :decimal + _(col.null).must_equal true + _(col.default).must_equal BigDecimal("12345.01") + _(obj.decimal_9_2).must_equal BigDecimal("12345.01") _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Decimal + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Decimal _(type.limit).must_be_nil - _(type.precision).must_equal 9 - _(type.scale).must_equal 2 + _(type.precision).must_equal 9 + _(type.scale).must_equal 2 obj.decimal_9_2 = "1234567.8901" _(obj.decimal_9_2).must_equal BigDecimal("1234567.89") obj.save! @@ -136,13 +138,13 @@ def assert_obj_set_and_save(attribute, value) it "decimal(16,4)" do col = column("decimal_16_4") - _(col.sql_type).must_equal "decimal(16,4)" - _(col.default).must_equal BigDecimal("1234567.89") - _(obj.decimal_16_4).must_equal BigDecimal("1234567.89") + _(col.sql_type).must_equal "decimal(16,4)" + _(col.default).must_equal BigDecimal("1234567.89") + _(obj.decimal_16_4).must_equal BigDecimal("1234567.89") _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type.precision).must_equal 16 - _(type.scale).must_equal 4 + _(type.precision).must_equal 16 + _(type.scale).must_equal 4 obj.decimal_16_4 = "1234567.8901001" _(obj.decimal_16_4).must_equal BigDecimal("1234567.8901") obj.save! @@ -151,39 +153,39 @@ def assert_obj_set_and_save(attribute, value) it "numeric(18,0)" do col = column("numeric_18_0") - _(col.sql_type).must_equal "numeric(18,0)" - _(col.type).must_equal :decimal - _(col.null).must_equal true - _(col.default).must_equal BigDecimal("191") - _(obj.numeric_18_0).must_equal BigDecimal("191") + _(col.sql_type).must_equal "numeric(18,0)" + _(col.type).must_equal :decimal + _(col.null).must_equal true + _(col.default).must_equal BigDecimal(191) + _(obj.numeric_18_0).must_equal BigDecimal(191) _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::DecimalWithoutScale + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::DecimalWithoutScale _(type.limit).must_be_nil - _(type.precision).must_equal 18 + _(type.precision).must_equal 18 _(type.scale).must_be_nil obj.numeric_18_0 = "192.1" - _(obj.numeric_18_0).must_equal BigDecimal("192") + _(obj.numeric_18_0).must_equal BigDecimal(192) obj.save! - _(obj.reload.numeric_18_0).must_equal BigDecimal("192") + _(obj.reload.numeric_18_0).must_equal BigDecimal(192) end it "numeric(36,2)" do col = column("numeric_36_2") - _(col.sql_type).must_equal "numeric(36,2)" - _(col.type).must_equal :decimal - _(col.null).must_equal true - _(col.default).must_equal BigDecimal("12345678901234567890.01") - _(obj.numeric_36_2).must_equal BigDecimal("12345678901234567890.01") + _(col.sql_type).must_equal "numeric(36,2)" + _(col.type).must_equal :decimal + _(col.null).must_equal true + _(col.default).must_equal BigDecimal("12345678901234567890.01") + _(obj.numeric_36_2).must_equal BigDecimal("12345678901234567890.01") _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Decimal + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Decimal _(type.limit).must_be_nil - _(type.precision).must_equal 36 - _(type.scale).must_equal 2 + _(type.precision).must_equal 36 + _(type.scale).must_equal 2 obj.numeric_36_2 = "192.123" _(obj.numeric_36_2).must_equal BigDecimal("192.12") obj.save! @@ -192,17 +194,17 @@ def assert_obj_set_and_save(attribute, value) it "money" do col = column("money") - _(col.sql_type).must_equal "money" - _(col.type).must_equal :money - _(col.null).must_equal true - _(col.default).must_equal BigDecimal("4.20") - _(obj.money).must_equal BigDecimal("4.20") + _(col.sql_type).must_equal "money" + _(col.type).must_equal :money + _(col.null).must_equal true + _(col.default).must_equal BigDecimal("4.20") + _(obj.money).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Money + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Money _(type.limit).must_be_nil - _(type.precision).must_equal 19 - _(type.scale).must_equal 4 + _(type.precision).must_equal 19 + _(type.scale).must_equal 4 obj.money = "922337203685477.58061" _(obj.money).must_equal BigDecimal("922337203685477.5806") obj.save! @@ -211,17 +213,17 @@ def assert_obj_set_and_save(attribute, value) it "smallmoney" do col = column("smallmoney") - _(col.sql_type).must_equal "smallmoney" - _(col.type).must_equal :smallmoney - _(col.null).must_equal true - _(col.default).must_equal BigDecimal("4.20") - _(obj.smallmoney).must_equal BigDecimal("4.20") + _(col.sql_type).must_equal "smallmoney" + _(col.type).must_equal :smallmoney + _(col.null).must_equal true + _(col.default).must_equal BigDecimal("4.20") + _(obj.smallmoney).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::SmallMoney + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::SmallMoney _(type.limit).must_be_nil - _(type.precision).must_equal 10 - _(type.scale).must_equal 4 + _(type.precision).must_equal 10 + _(type.scale).must_equal 4 obj.smallmoney = "214748.36461" _(obj.smallmoney).must_equal BigDecimal("214748.3646") obj.save! @@ -234,14 +236,14 @@ def assert_obj_set_and_save(attribute, value) it "float" do col = column("float") - _(col.sql_type).must_equal "float" - _(col.type).must_equal :float - _(col.null).must_equal true - _(col.default).must_equal 123.00000001 - _(obj.float).must_equal 123.00000001 + _(col.sql_type).must_equal "float" + _(col.type).must_equal :float + _(col.null).must_equal true + _(col.default).must_equal 123.00000001 + _(obj.float).must_equal 123.00000001 _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Float + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Float _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil @@ -253,14 +255,14 @@ def assert_obj_set_and_save(attribute, value) it "real" do col = column("real") - _(col.sql_type).must_equal "real" - _(col.type).must_equal :real - _(col.null).must_equal true - _(col.default).must_be_close_to 123.45, 0.01 - _(obj.real).must_be_close_to 123.45, 0.01 + _(col.sql_type).must_equal "real" + _(col.type).must_equal :real + _(col.null).must_equal true + _(col.default).must_be_close_to 123.45, 0.01 + _(obj.real).must_be_close_to 123.45, 0.01 _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Real + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Real _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil @@ -274,14 +276,14 @@ def assert_obj_set_and_save(attribute, value) it "date" do col = column("date") - _(col.sql_type).must_equal "date" - _(col.type).must_equal :date - _(col.null).must_equal true - _(col.default).must_equal Date.civil(1, 1, 1) - _(obj.date).must_equal Date.civil(1, 1, 1) + _(col.sql_type).must_equal "date" + _(col.type).must_equal :date + _(col.null).must_equal true + _(col.default).must_equal Date.civil(1, 1, 1) + _(obj.date).must_equal Date.civil(1, 1, 1) _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Date + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Date _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil @@ -305,22 +307,22 @@ def assert_obj_set_and_save(attribute, value) assert_obj_set_and_save :date, Date.civil(1972, 4, 14) # Can accept and cast time objects. obj.date = Time.utc(2010, 4, 14, 12, 34, 56, 3000) - _(obj.date).must_equal Date.civil(2010, 4, 14) + _(obj.date).must_equal Date.civil(2010, 4, 14) obj.save! _(obj.reload.date).must_equal Date.civil(2010, 4, 14) end it "datetime" do col = column("datetime") - _(col.sql_type).must_equal "datetime" - _(col.type).must_equal :datetime - _(col.null).must_equal true + _(col.sql_type).must_equal "datetime" + _(col.type).must_equal :datetime + _(col.null).must_equal true time = Time.utc 1753, 1, 1, 0, 0, 0, 123000 - _(col.default).must_equal time, "Microseconds were <#{col.default.usec}> vs <123000>" - _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <123000>" + _(col.default).must_equal time, "Microseconds were <#{col.default.usec}> vs <123000>" + _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <123000>" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::DateTime + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::DateTime _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil @@ -358,15 +360,15 @@ def assert_obj_set_and_save(attribute, value) it "datetime2" do col = column("datetime2_7") - _(col.sql_type).must_equal "datetime2(7)" - _(col.type).must_equal :datetime - _(col.null).must_equal true + _(col.sql_type).must_equal "datetime2(7)" + _(col.type).must_equal :datetime + _(col.null).must_equal true time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(999999900, 1000) - _(col.default).must_equal time, "Nanoseconds were <#{col.default.nsec}> vs <999999900>" - _(obj.datetime2_7).must_equal time, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" + _(col.default).must_equal time, "Nanoseconds were <#{col.default.nsec}> vs <999999900>" + _(obj.datetime2_7).must_equal time, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::DateTime2 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::DateTime2 _(type.limit).must_be_nil _(type.precision).must_equal 7 _(type.scale).must_be_nil @@ -398,7 +400,8 @@ def assert_obj_set_and_save(attribute, value) _(col.fetch_cast_type(connection).precision).must_equal 3 obj.datetime2_3 = time _(obj.datetime2_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.datetime2_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" _(obj).must_equal obj.class.where(datetime2_3: time).first # datetime2_1 @@ -406,7 +409,8 @@ def assert_obj_set_and_save(attribute, value) _(col.fetch_cast_type(connection).precision).must_equal 1 obj.datetime2_1 = time _(obj.datetime2_1).must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.datetime2_1).must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" _(obj).must_equal obj.class.where(datetime2_1: time).first # datetime2_0 @@ -415,21 +419,22 @@ def assert_obj_set_and_save(attribute, value) time = Time.utc 2016, 4, 19, 16, 45, 40, 771036 obj.datetime2_0 = time _(obj.datetime2_0).must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.datetime2_0).must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" _(obj).must_equal obj.class.where(datetime2_0: time).first end it "datetimeoffset" do col = column("datetimeoffset_7") - _(col.sql_type).must_equal "datetimeoffset(7)" - _(col.type).must_equal :datetimeoffset - _(col.null).must_equal true - _(col.default).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" - _(obj.datetimeoffset_7).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" + _(col.sql_type).must_equal "datetimeoffset(7)" + _(col.type).must_equal :datetimeoffset + _(col.null).must_equal true + _(col.default).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" + _(obj.datetimeoffset_7).must_equal Time.new(1984, 1, 24, 4, 20, 0, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::DateTimeOffset + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::DateTimeOffset _(type.limit).must_be_nil _(type.precision).must_equal 7 _(type.scale).must_be_nil @@ -468,14 +473,14 @@ def assert_obj_set_and_save(attribute, value) it "smalldatetime" do col = column("smalldatetime") - _(col.sql_type).must_equal "smalldatetime" - _(col.type).must_equal :smalldatetime - _(col.null).must_equal true - _(col.default).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0) - _(obj.smalldatetime).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0) + _(col.sql_type).must_equal "smalldatetime" + _(col.type).must_equal :smalldatetime + _(col.null).must_equal true + _(col.default).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0) + _(obj.smalldatetime).must_equal Time.utc(1901, 1, 1, 15, 45, 0, 0) _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::SmallDateTime + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::SmallDateTime _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil @@ -490,92 +495,101 @@ def assert_obj_set_and_save(attribute, value) it "time(7)" do col = column("time_7") - _(col.sql_type).must_equal "time(7)" - _(col.type).must_equal :time - _(col.null).must_equal true - _(col.default).must_equal Time.utc(1900, 1, 1, 4, 20, 0, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" + _(col.sql_type).must_equal "time(7)" + _(col.type).must_equal :time + _(col.null).must_equal true + _(col.default).must_equal Time.utc(1900, 1, 1, 4, 20, 0, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Time + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 7 _(type.scale).must_be_nil # Time's #usec precision (low micro) obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, 300) - _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" - _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" - obj.save!; obj.reload - _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" - _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + obj.save! + obj.reload + _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" # Time's #usec precision (high micro) obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, 234567) _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" # Time's #usec precision (high nano rounded) obj.time_7 = Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321545, 1000)) _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.time_7).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" end it "time(2)" do col = column("time_2") - _(col.sql_type).must_equal "time(2)" - _(col.type).must_equal :time - _(col.null).must_equal true + _(col.sql_type).must_equal "time(2)" + _(col.type).must_equal :time + _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Time + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 2 _(type.scale).must_be_nil # Always uses TinyTDS/Windows 2000-01-01 convention too. obj.time_2 = Time.utc(2015, 1, 10, 15, 45, 0, 0) _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0) - obj.save!; obj.reload + obj.save! + obj.reload _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0) # Time's #usec precision (barely in 2 precision equal to 0.03 seconds) obj.time_2 = Time.utc(2000, 1, 1, 15, 45, 0, 30000) _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" # Time's #usec precision (below 2 precision) obj.time_2 = Time.utc(2000, 1, 1, 15, 45, 0, 4000) _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.time_2).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" end it "time using default precision" do col = column("time_default") - _(col.sql_type).must_equal "time(7)" - _(col.type).must_equal :time - _(col.null).must_equal true - _(col.default).must_equal Time.utc(1900, 1, 1, 15, 3, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>" + _(col.sql_type).must_equal "time(7)" + _(col.type).must_equal :time + _(col.null).must_equal true + _(col.default).must_equal Time.utc(1900, 1, 1, 15, 3, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Time + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 7 _(type.scale).must_be_nil # Time's #usec precision (low micro) obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, 300) - _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" - _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" - obj.save!; obj.reload - _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" - _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" + _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" + _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" + obj.save! + obj.reload + _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" + _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" # Time's #usec precision (high micro) obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, 234567) _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" # Time's #usec precision (high nano rounded) obj.time_default = Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321545, 1000)) _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.time_default).must_equal Time.utc(2000, 1, 1, 15, 45, 0, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" end @@ -583,15 +597,15 @@ def assert_obj_set_and_save(attribute, value) it "char(10)" do col = column("char_10") - _(col.sql_type).must_equal "char(10)" - _(col.type).must_equal :char - _(col.null).must_equal true - _(col.default).must_equal "1234567890" - _(obj.char_10).must_equal "1234567890" + _(col.sql_type).must_equal "char(10)" + _(col.type).must_equal :char + _(col.null).must_equal true + _(col.default).must_equal "1234567890" + _(obj.char_10).must_equal "1234567890" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Char - _(type.limit).must_equal 10 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Char + _(type.limit).must_equal 10 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -603,15 +617,15 @@ def assert_obj_set_and_save(attribute, value) it "varchar(50)" do col = column("varchar_50") - _(col.sql_type).must_equal "varchar(50)" - _(col.type).must_equal :varchar - _(col.null).must_equal true - _(col.default).must_equal "test varchar_50" - _(obj.varchar_50).must_equal "test varchar_50" + _(col.sql_type).must_equal "varchar(50)" + _(col.type).must_equal :varchar + _(col.null).must_equal true + _(col.default).must_equal "test varchar_50" + _(obj.varchar_50).must_equal "test varchar_50" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Varchar - _(type.limit).must_equal 50 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Varchar + _(type.limit).must_equal 50 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -620,15 +634,15 @@ def assert_obj_set_and_save(attribute, value) it "varchar(max)" do col = column("varchar_max") - _(col.sql_type).must_equal "varchar(max)" - _(col.type).must_equal :varchar_max - _(col.null).must_equal true - _(col.default).must_equal "test varchar_max" - _(obj.varchar_max).must_equal "test varchar_max" + _(col.sql_type).must_equal "varchar(max)" + _(col.type).must_equal :varchar_max + _(col.null).must_equal true + _(col.default).must_equal "test varchar_max" + _(obj.varchar_max).must_equal "test varchar_max" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::VarcharMax - _(type.limit).must_equal 2_147_483_647 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::VarcharMax + _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -637,15 +651,15 @@ def assert_obj_set_and_save(attribute, value) it "text" do col = column("text") - _(col.sql_type).must_equal "text" - _(col.type).must_equal :text_basic - _(col.null).must_equal true - _(col.default).must_equal "test text" - _(obj.text).must_equal "test text" + _(col.sql_type).must_equal "text" + _(col.type).must_equal :text_basic + _(col.null).must_equal true + _(col.default).must_equal "test text" + _(obj.text).must_equal "test text" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Text - _(type.limit).must_equal 2_147_483_647 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Text + _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -656,15 +670,15 @@ def assert_obj_set_and_save(attribute, value) it "nchar(10)" do col = column("nchar_10") - _(col.sql_type).must_equal "nchar(10)" - _(col.type).must_equal :nchar - _(col.null).must_equal true - _(col.default).must_equal "12345678åå" - _(obj.nchar_10).must_equal "12345678åå" + _(col.sql_type).must_equal "nchar(10)" + _(col.type).must_equal :nchar + _(col.null).must_equal true + _(col.default).must_equal "12345678åå" + _(obj.nchar_10).must_equal "12345678åå" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::UnicodeChar - _(type.limit).must_equal 10 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::UnicodeChar + _(type.limit).must_equal 10 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -676,15 +690,15 @@ def assert_obj_set_and_save(attribute, value) it "nvarchar(50)" do col = column("nvarchar_50") - _(col.sql_type).must_equal "nvarchar(50)" - _(col.type).must_equal :string - _(col.null).must_equal true - _(col.default).must_equal "test nvarchar_50 åå" - _(obj.nvarchar_50).must_equal "test nvarchar_50 åå" + _(col.sql_type).must_equal "nvarchar(50)" + _(col.type).must_equal :string + _(col.null).must_equal true + _(col.default).must_equal "test nvarchar_50 åå" + _(obj.nvarchar_50).must_equal "test nvarchar_50 åå" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::UnicodeVarchar - _(type.limit).must_equal 50 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::UnicodeVarchar + _(type.limit).must_equal 50 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -693,15 +707,15 @@ def assert_obj_set_and_save(attribute, value) it "nvarchar(max)" do col = column("nvarchar_max") - _(col.sql_type).must_equal "nvarchar(max)" - _(col.type).must_equal :text - _(col.null).must_equal true - _(col.default).must_equal "test nvarchar_max åå" - _(obj.nvarchar_max).must_equal "test nvarchar_max åå" + _(col.sql_type).must_equal "nvarchar(max)" + _(col.type).must_equal :text + _(col.null).must_equal true + _(col.default).must_equal "test nvarchar_max åå" + _(obj.nvarchar_max).must_equal "test nvarchar_max åå" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::UnicodeVarcharMax - _(type.limit).must_equal 2_147_483_647 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::UnicodeVarcharMax + _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -710,15 +724,15 @@ def assert_obj_set_and_save(attribute, value) it "ntext" do col = column("ntext") - _(col.sql_type).must_equal "ntext" - _(col.type).must_equal :ntext - _(col.null).must_equal true - _(col.default).must_equal "test ntext åå" - _(obj.ntext).must_equal "test ntext åå" + _(col.sql_type).must_equal "ntext" + _(col.type).must_equal :ntext + _(col.null).must_equal true + _(col.default).must_equal "test ntext åå" + _(obj.ntext).must_equal "test ntext åå" _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::UnicodeText - _(type.limit).must_equal 2_147_483_647 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::UnicodeText + _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -728,18 +742,18 @@ def assert_obj_set_and_save(attribute, value) # Binary Strings let(:binary_file) { File.join ARTest::SQLServer.test_root_sqlserver, "fixtures", "1px.gif" } - let(:binary_data) { File.open(binary_file, "rb") { |f| f.read } } + let(:binary_data) { File.binread(binary_file) } it "binary(49)" do col = column("binary_49") - _(col.sql_type).must_equal "binary(49)" - _(col.type).must_equal :binary_basic - _(col.null).must_equal true + _(col.sql_type).must_equal "binary(49)" + _(col.type).must_equal :binary_basic + _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Binary - _(type.limit).must_equal 49 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Binary + _(type.limit).must_equal 49 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -753,14 +767,14 @@ def assert_obj_set_and_save(attribute, value) it "varbinary(49)" do col = column("varbinary_49") - _(col.sql_type).must_equal "varbinary(49)" - _(col.type).must_equal :varbinary - _(col.null).must_equal true + _(col.sql_type).must_equal "varbinary(49)" + _(col.type).must_equal :varbinary + _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Varbinary - _(type.limit).must_equal 49 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Varbinary + _(type.limit).must_equal 49 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -774,14 +788,14 @@ def assert_obj_set_and_save(attribute, value) it "varbinary(max)" do col = column("varbinary_max") - _(col.sql_type).must_equal "varbinary(max)" - _(col.type).must_equal :binary - _(col.null).must_equal true + _(col.sql_type).must_equal "varbinary(max)" + _(col.type).must_equal :binary + _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::VarbinaryMax - _(type.limit).must_equal 2_147_483_647 + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::VarbinaryMax + _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. @@ -793,43 +807,46 @@ def assert_obj_set_and_save(attribute, value) it "uniqueidentifier" do col = column("uniqueidentifier") - _(col.sql_type).must_equal "uniqueidentifier" - _(col.type).must_equal :uuid - _(col.null).must_equal true + _(col.sql_type).must_equal "uniqueidentifier" + _(col.type).must_equal :uuid + _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_equal "newid()" type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Uuid + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. obj.uniqueidentifier = "this will not qualify as valid" _(obj.uniqueidentifier).must_be_nil - obj.save!; obj.reload - _(obj.uniqueidentifier).must_match Type::Uuid::ACCEPTABLE_UUID + obj.save! + obj.reload + _(obj.uniqueidentifier).must_match ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID obj.uniqueidentifier = "6F9619FF-8B86-D011-B42D-00C04FC964FF" _(obj.uniqueidentifier).must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" - obj.save!; obj.reload + obj.save! + obj.reload _(obj.uniqueidentifier).must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" end it "timestamp" do col = column("timestamp") - _(col.sql_type).must_equal "timestamp" - _(col.type).must_equal :ss_timestamp - _(col.null).must_equal true + _(col.sql_type).must_equal "timestamp" + _(col.type).must_equal :ss_timestamp + _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil type = col.fetch_cast_type(connection) - _(type).must_be_instance_of Type::Timestamp + _(type).must_be_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Type::Timestamp _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic read. _(obj.timestamp).must_be_nil - obj.save!; obj.reload - _(obj.timestamp).must_match %r|\000| + obj.save! + obj.reload + _(obj.timestamp).must_match %r{\000} obj.timestamp # Can set another attribute obj.uniqueidentifier = "6F9619FF-8B86-D011-B42D-00C04FC964FF" diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 1cb05cc3e..da3e8e8b5 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -15,7 +15,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase end it "affect rows" do - topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } + topic_data = {1 => {"content" => "1 updated"}, 2 => {"content" => "2 updated"}} updated = Topic.update(topic_data.keys, topic_data.values) assert_equal 2, updated.size assert_equal "1 updated", Topic.find(1).content @@ -23,16 +23,18 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase assert_equal 2, Topic.delete([1, 2]) end - it "allow usage of :database connection option to remove setting from dsn" do - assert_equal "activerecord_unittest", connection.current_database - begin - connection.use_database("activerecord_unittest2") - assert_equal "activerecord_unittest2", connection.current_database - ensure - connection.use_database - assert_equal "activerecord_unittest", connection.current_database, "Would default back to connection options" + unless connection_sqlserver_azure? + it "allow usage of :database connection option to remove setting from dsn" do + assert_equal "activerecord_unittest", connection.current_database + begin + connection.use_database("activerecord_unittest2") + assert_equal "activerecord_unittest2", connection.current_database + ensure + connection.use_database + assert_equal "activerecord_unittest", connection.current_database, "Would default back to connection options" + end end - end unless connection_sqlserver_azure? + end describe "Connection management" do it "set spid on connect" do @@ -60,6 +62,8 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase private def disconnect_raw_connection! - connection.raw_connection.close rescue nil + connection.raw_connection.close + rescue + nil end end diff --git a/test/cases/enum_test_sqlserver.rb b/test/cases/enum_test_sqlserver.rb index 713056f85..8089e7068 100644 --- a/test/cases/enum_test_sqlserver.rb +++ b/test/cases/enum_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class EnumTestSQLServer < ActiveRecord::TestCase - # Check that enums are supported for all string types. # For each type we check: cast, serialize, and update by declaration. # We create a custom class for each type to test. @@ -11,27 +10,27 @@ class EnumTestSQLServer < ActiveRecord::TestCase describe "support #{col_name} enums" do let(:klass) do Class.new(ActiveRecord::Base) do - self.table_name = 'sst_datatypes' + self.table_name = "sst_datatypes" - enum col_name, { alpha: "A", beta: "B" } + enum col_name, {alpha: "A", beta: "B"} end end it "type.cast" do type = klass.type_for_attribute(col_name) - assert_equal "alpha", type.cast('A') - assert_equal "beta", type.cast('B') + assert_equal "alpha", type.cast("A") + assert_equal "beta", type.cast("B") end it "type.serialize" do type = klass.type_for_attribute(col_name) - assert_equal 'A', type.serialize('A') - assert_equal 'B', type.serialize('B') + assert_equal "A", type.serialize("A") + assert_equal "B", type.serialize("B") - assert_equal 'A', type.serialize(:alpha) - assert_equal 'B', type.serialize(:beta) + assert_equal "A", type.serialize(:alpha) + assert_equal "B", type.serialize(:beta) end it "update by declaration" do diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index d7f58965c..a47c8aa15 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -49,7 +49,7 @@ def transaction_with_procedure_and_return end end - it 'test deprecation with transaction return when executing procedure' do + it "test deprecation with transaction return when executing procedure" do assert_not_deprecated(ActiveRecord.deprecator) do transaction_with_procedure_and_return end diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 2b55bd25a..3a4dcc3ee 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -49,7 +49,7 @@ class FetchTestSqlserver < ActiveRecord::TestCase query = Book.from(from_sql).order(:id).limit(5) assert_equal query.to_sql, "SELECT [books].* FROM (SELECT [books].* FROM [books]) [books] ORDER BY [books].[id] ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY" - assert_equal query.to_a.count, 5 + assert_equal query.to_a.size, 5 end it "exception thrown if FROM subquery is provided without an order" do diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index c42e9c7c0..411d4c08d 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -25,9 +25,9 @@ module ActiveRecord class TestCase < ActiveSupport::TestCase SQLServer = ActiveRecord::ConnectionAdapters::SQLServer - include ARTest::SQLServer::ConnectionReflection, - ActiveSupport::Testing::Stream, - ARTest::SQLServer::QueryAssertions + include ARTest::SQLServer::QueryAssertions + include ActiveSupport::Testing::Stream + include ARTest::SQLServer::ConnectionReflection let(:logger) { ActiveRecord::Base.logger } diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index 09acec01f..6e3a72082 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -8,27 +8,29 @@ class IndexTestSQLServer < ActiveRecord::TestCase t.column :foo, :string, limit: 100 t.column :bar, :string, limit: 100 t.string :first_name - t.string :last_name, limit: 100 - t.string :key, limit: 100 + t.string :last_name, limit: 100 + t.string :key, limit: 100 t.boolean :administrator end end after do - connection.drop_table :testings rescue nil + connection.drop_table :testings + rescue + nil end it "add index with order" do assert_queries_match(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do - connection.add_index "testings", ["last_name"], order: { last_name: :desc } + connection.add_index "testings", ["last_name"], order: {last_name: :desc} connection.remove_index "testings", ["last_name"] end assert_queries_match(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do - connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc } + connection.add_index "testings", ["last_name", "first_name"], order: {last_name: :desc} connection.remove_index "testings", ["last_name", "first_name"] end assert_queries_match(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\] ASC\)/i) do - connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc, first_name: :asc } + connection.add_index "testings", ["last_name", "first_name"], order: {last_name: :desc, first_name: :asc} connection.remove_index "testings", ["last_name", "first_name"] end end diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb index 501210a12..a1691b861 100644 --- a/test/cases/json_test_sqlserver.rb +++ b/test/cases/json_test_sqlserver.rb @@ -5,19 +5,19 @@ if ActiveRecord::Base.lease_connection.supports_json? class JsonTestSQLServer < ActiveRecord::TestCase before do - @o1 = SSTestDatatypeMigrationJson.create! json_col: { "a" => "a", "b" => "b", "c" => "c" } - @o2 = SSTestDatatypeMigrationJson.create! json_col: { "a" => nil, "b" => "b", "c" => "c" } - @o3 = SSTestDatatypeMigrationJson.create! json_col: { "x" => 1, "y" => 2, "z" => 3 } - @o4 = SSTestDatatypeMigrationJson.create! json_col: { "array" => [1, 2, 3] } + @o1 = SSTestDatatypeMigrationJson.create! json_col: {"a" => "a", "b" => "b", "c" => "c"} + @o2 = SSTestDatatypeMigrationJson.create! json_col: {"a" => nil, "b" => "b", "c" => "c"} + @o3 = SSTestDatatypeMigrationJson.create! json_col: {"x" => 1, "y" => 2, "z" => 3} + @o4 = SSTestDatatypeMigrationJson.create! json_col: {"array" => [1, 2, 3]} @o5 = SSTestDatatypeMigrationJson.create! json_col: nil end it "can return and save JSON data" do - _(SSTestDatatypeMigrationJson.find(@o1.id).json_col).must_equal({ "a" => "a", "b" => "b", "c" => "c" }) - @o1.json_col = { "a" => "a" } - _(@o1.json_col).must_equal({ "a" => "a" }) + _(SSTestDatatypeMigrationJson.find(@o1.id).json_col).must_equal({"a" => "a", "b" => "b", "c" => "c"}) + @o1.json_col = {"a" => "a"} + _(@o1.json_col).must_equal({"a" => "a"}) @o1.save! - _(@o1.reload.json_col).must_equal({ "a" => "a" }) + _(@o1.reload.json_col).must_equal({"a" => "a"}) end it "can use ISJSON function" do diff --git a/test/cases/lateral_test_sqlserver.rb b/test/cases/lateral_test_sqlserver.rb index fb1660110..c672576b3 100644 --- a/test/cases/lateral_test_sqlserver.rb +++ b/test/cases/lateral_test_sqlserver.rb @@ -7,7 +7,7 @@ class LateralTestSQLServer < ActiveRecord::TestCase fixtures :posts, :authors - it 'uses OUTER APPLY for OUTER JOIN LATERAL' do + it "uses OUTER APPLY for OUTER JOIN LATERAL" do post = Arel::Table.new(:posts) author = Arel::Table.new(:authors) subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42)) @@ -21,7 +21,7 @@ class LateralTestSQLServer < ActiveRecord::TestCase assert_equal results.length, 1 end - it 'uses CROSS APPLY for INNER JOIN LATERAL' do + it "uses CROSS APPLY for INNER JOIN LATERAL" do post = Arel::Table.new(:posts) author = Arel::Table.new(:authors) subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42)) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 0ffe20861..b829c8632 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -21,8 +21,8 @@ class MigrationTestSQLServer < ActiveRecord::TestCase begin migrations_dir = File.join ARTest::SQLServer.migrations_root, "transaction_table" quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } - rescue Exception => e - assert_match %r|this and all later migrations canceled|, e.message + rescue => e + assert_match %r{this and all later migrations canceled}, e.message end _(connection.tables).wont_include @trans_test_table1 _(connection.tables).wont_include @trans_test_table2 @@ -45,9 +45,9 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it "not drop the default constraint if just renaming" do find_default = lambda do - connection.execute_procedure(:sp_helpconstraint, "sst_string_defaults", "nomsg").select do |row| + connection.execute_procedure(:sp_helpconstraint, "sst_string_defaults", "nomsg").reverse.find do |row| row["constraint_type"] == "DEFAULT on column string_with_pretend_paren_three" - end.last + end end default_before = find_default.call connection.change_column :sst_string_defaults, :string_with_pretend_paren_three, :string, limit: 255 @@ -68,7 +68,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase assert_nothing_raised { connection.change_column :sst_string_collation, :string_with_collation, :varchar, collation: :SQL_Latin1_General_CP437_BIN } SstStringCollation.reset_column_information - assert_equal "SQL_Latin1_General_CP437_BIN", SstStringCollation.columns_hash['string_with_collation'].collation + assert_equal "SQL_Latin1_General_CP437_BIN", SstStringCollation.columns_hash["string_with_collation"].collation end end @@ -78,7 +78,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase schemas = connection.exec_query("select name from sys.schemas").to_a - assert_includes schemas, { "name" => "some schema" } + assert_includes schemas, {"name" => "some schema"} end it "creates a new schema with an owner" do @@ -86,7 +86,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase schemas = connection.exec_query("select name, principal_id from sys.schemas").to_a - assert_includes schemas, { "name" => "some schema", "principal_id" => 2 } + assert_includes schemas, {"name" => "some schema", "principal_id" => 2} end end @@ -106,18 +106,18 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it "drops a schema" do schemas = connection.exec_query("select name from sys.schemas").to_a - assert_includes schemas, { "name" => "some schema" } + assert_includes schemas, {"name" => "some schema"} connection.drop_schema("some schema") schemas = connection.exec_query("select name from sys.schemas").to_a - refute_includes schemas, { "name" => "some schema" } + refute_includes schemas, {"name" => "some schema"} end end - describe 'creating stored procedure' do - it 'stored procedure contains inserts are created successfully' do + describe "creating stored procedure" do + it "stored procedure contains inserts are created successfully" do sql = <<-SQL CREATE OR ALTER PROCEDURE do_some_task AS @@ -126,7 +126,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase CREATE TABLE SomeTableName (SomeNum int PRIMARY KEY CLUSTERED); INSERT INTO SomeTableName(SomeNum) VALUES(1); END - SQL + SQL assert_nothing_raised { connection.execute(sql) } ensure diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index ecb1553d8..13640e6c0 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -13,7 +13,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end it "uses with updlock by default" do - assert_queries_match %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)| do + assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)} do _(Person.lock(true).to_a).must_equal Person.all.to_a end end @@ -47,32 +47,32 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end it "can add a custom lock directive" do - assert_queries_match %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do + assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)} do Person.lock("WITH(HOLDLOCK, ROWLOCK)").load end end describe "joining tables" do it "joined tables use updlock by default" do - assert_queries_match %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) INNER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do + assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) INNER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do Person.lock(true).joins(:readers).load end end it "joined tables can use custom lock directive" do - assert_queries_match %r|SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) INNER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do + assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) INNER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do Person.lock("WITH(NOLOCK)").joins(:readers).load end end it "left joined tables use updlock by default" do - assert_queries_match %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do + assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do Person.lock(true).left_joins(:readers).load end end it "left joined tables can use custom lock directive" do - assert_queries_match %r|SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) LEFT OUTER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]| do + assert_queries_match %r{SELECT \[people\]\.\* FROM \[people\] WITH\(NOLOCK\) LEFT OUTER JOIN \[readers\] WITH\(NOLOCK\)\s+ON \[readers\]\.\[person_id\] = \[people\]\.\[id\]} do Person.lock("WITH(NOLOCK)").left_joins(:readers).load end end diff --git a/test/cases/primary_keys_test_sqlserver.rb b/test/cases/primary_keys_test_sqlserver.rb index 8edf3dcfe..1de09790d 100644 --- a/test/cases/primary_keys_test_sqlserver.rb +++ b/test/cases/primary_keys_test_sqlserver.rb @@ -67,7 +67,7 @@ class Widget < ActiveRecord::Base assert_not_predicate column, :bigint? schema = dump_table_schema "widgets" - assert_match %r/create_table "widgets", id: :integer, force: :cascade do/, schema + assert_match %r{create_table "widgets", id: :integer, force: :cascade do}, schema end test "bigint primary key without default" do @@ -79,7 +79,7 @@ class Widget < ActiveRecord::Base assert_predicate column, :bigint? schema = dump_table_schema "widgets" - assert_match %r/create_table "widgets", force: :cascade do/, schema + assert_match %r{create_table "widgets", force: :cascade do}, schema end test "don't set identity to integer and bigint when there is a default" do @@ -91,13 +91,13 @@ class Widget < ActiveRecord::Base assert_not_predicate column, :is_identity? schema = dump_table_schema "widgets" - assert_match %r/create_table "widgets", id: :bigint, default: nil, force: :cascade do/, schema + assert_match %r{create_table "widgets", id: :bigint, default: nil, force: :cascade do}, schema column = @connection.columns(:barcodes).find { |c| c.name == "id" } assert_predicate column, :is_primary? assert_not_predicate column, :is_identity? schema = dump_table_schema "barcodes" - assert_match %r/create_table "barcodes", id: :integer, default: nil, force: :cascade do/, schema + assert_match %r{create_table "barcodes", id: :integer, default: nil, force: :cascade do}, schema end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index ea1074d76..1a6d7ec8a 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -8,15 +8,15 @@ class SQLServerRakeTest < ActiveRecord::TestCase cattr_accessor :azure_skip self.azure_skip = connection_sqlserver_azure? - let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks } - let(:new_database) { "activerecord_unittest_tasks" } + let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks } + let(:new_database) { "activerecord_unittest_tasks" } let(:default_configuration) { ARTest.test_configuration_hashes["arunit"] } - let(:configuration) { default_configuration.merge("database" => new_database) } - let(:db_config) { ActiveRecord::Base.configurations.resolve(configuration) } + let(:configuration) { default_configuration.merge("database" => new_database) } + let(:db_config) { ActiveRecord::Base.configurations.resolve(configuration) } before { skip "on azure" if azure_skip } before { disconnect! unless azure_skip } - after { reconnect unless azure_skip } + after { reconnect unless azure_skip } private @@ -28,12 +28,20 @@ def reconnect config = default_configuration if connection_sqlserver_azure? ActiveRecord::Base.establish_connection(config.merge("database" => "master")) - connection.drop_database(new_database) rescue nil + begin + connection.drop_database(new_database) + rescue + nil + end disconnect! ActiveRecord::Base.establish_connection(config) else ActiveRecord::Base.establish_connection(config) - connection.drop_database(new_database) rescue nil + begin + connection.drop_database(new_database) + rescue + nil + end end end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 9e02d4b26..39f4e0dc1 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -6,52 +6,52 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase before { all_tables } - let(:all_tables) { ActiveRecord::Base.lease_connection.tables } - let(:schema) { @generated_schema } + let(:all_tables) { ActiveRecord::Base.lease_connection.tables } + let(:schema) { @generated_schema } it "sst_datatypes" do generate_schema_for_table "sst_datatypes" - assert_line :bigint, type: "bigint", default: 42 - assert_line :int, type: "integer", default: 42 - assert_line :smallint, type: "integer", limit: 2, default: 42 - assert_line :tinyint, type: "integer", limit: 1, default: 42 - assert_line :bit, type: "boolean", default: true - assert_line :decimal_9_2, type: "decimal", precision: 9, scale: 2, default: 12345.01 - assert_line :numeric_18_0, type: "decimal", precision: 18, default: 191 - assert_line :numeric_36_2, type: "decimal", precision: 36, scale: 2, default: 12345678901234567890.01 - assert_line :money, type: "money", precision: 19, scale: 4, default: 4.2 - assert_line :smallmoney, type: "smallmoney", precision: 10, scale: 4, default: 4.2 + assert_line :bigint, type: "bigint", default: 42 + assert_line :int, type: "integer", default: 42 + assert_line :smallint, type: "integer", limit: 2, default: 42 + assert_line :tinyint, type: "integer", limit: 1, default: 42 + assert_line :bit, type: "boolean", default: true + assert_line :decimal_9_2, type: "decimal", precision: 9, scale: 2, default: 12345.01 + assert_line :numeric_18_0, type: "decimal", precision: 18, default: 191 + assert_line :numeric_36_2, type: "decimal", precision: 36, scale: 2, default: 12345678901234567890.01 + assert_line :money, type: "money", precision: 19, scale: 4, default: 4.2 + assert_line :smallmoney, type: "smallmoney", precision: 10, scale: 4, default: 4.2 # Approximate Numerics - assert_line :float, type: "float", default: 123.00000001 - assert_line :real, type: "real", default: 123.45 + assert_line :float, type: "float", default: 123.00000001 + assert_line :real, type: "real", default: 123.45 # Date and Time - assert_line :date, type: "date", default: "01-01-0001" - assert_line :datetime, type: "datetime", precision: nil, default: "01-01-1753 00:00:00.123" - assert_line :datetime2_7, type: "datetime", precision: 7, default: "12-31-9999 23:59:59.9999999" - assert_line :datetime2_3, type: "datetime", precision: 3 - assert_line :datetime2_1, type: "datetime", precision: 1 - assert_line :smalldatetime, type: "smalldatetime", default: "01-01-1901 15:45:00.0" - assert_line :time_7, type: "time", precision: 7, default: "04:20:00.2883215" - assert_line :time_2, type: "time", precision: 2 - assert_line :time_default, type: "time", precision: 7, default: "15:03:42.0621978" + assert_line :date, type: "date", default: "01-01-0001" + assert_line :datetime, type: "datetime", precision: nil, default: "01-01-1753 00:00:00.123" + assert_line :datetime2_7, type: "datetime", precision: 7, default: "12-31-9999 23:59:59.9999999" + assert_line :datetime2_3, type: "datetime", precision: 3 + assert_line :datetime2_1, type: "datetime", precision: 1 + assert_line :smalldatetime, type: "smalldatetime", default: "01-01-1901 15:45:00.0" + assert_line :time_7, type: "time", precision: 7, default: "04:20:00.2883215" + assert_line :time_2, type: "time", precision: 2 + assert_line :time_default, type: "time", precision: 7, default: "15:03:42.0621978" # Character Strings - assert_line :char_10, type: "char", limit: 10, default: "1234567890" - assert_line :varchar_50, type: "varchar", limit: 50, default: "test varchar_50" - assert_line :varchar_max, type: "varchar_max", default: "test varchar_max" - assert_line :text, type: "text_basic", default: "test text" + assert_line :char_10, type: "char", limit: 10, default: "1234567890" + assert_line :varchar_50, type: "varchar", limit: 50, default: "test varchar_50" + assert_line :varchar_max, type: "varchar_max", default: "test varchar_max" + assert_line :text, type: "text_basic", default: "test text" # Unicode Character Strings - assert_line :nchar_10, type: "nchar", limit: 10, default: "12345678åå" - assert_line :nvarchar_50, type: "string", limit: 50, default: "test nvarchar_50 åå" - assert_line :nvarchar_max, type: "text", default: "test nvarchar_max åå" - assert_line :ntext, type: "ntext", default: "test ntext åå" + assert_line :nchar_10, type: "nchar", limit: 10, default: "12345678åå" + assert_line :nvarchar_50, type: "string", limit: 50, default: "test nvarchar_50 åå" + assert_line :nvarchar_max, type: "text", default: "test nvarchar_max åå" + assert_line :ntext, type: "ntext", default: "test ntext åå" # Binary Strings - assert_line :binary_49, type: "binary_basic", limit: 49 - assert_line :varbinary_49, type: "varbinary", limit: 49 - assert_line :varbinary_max, type: "binary" + assert_line :binary_49, type: "binary_basic", limit: 49 + assert_line :varbinary_49, type: "varbinary", limit: 49 + assert_line :varbinary_max, type: "binary" # Other Data Types - assert_line :uniqueidentifier, type: "uuid", default: -> { "newid()" } - assert_line :timestamp, type: "ss_timestamp" + assert_line :uniqueidentifier, type: "uuid", default: -> { "newid()" } + assert_line :timestamp, type: "ss_timestamp" end it "sst_datatypes_migration" do @@ -59,80 +59,80 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase generate_schema_for_table "sst_datatypes_migration" # Simple Rails conventions - _(columns["integer_col"].sql_type).must_equal "int(4)" - _(columns["bigint_col"].sql_type).must_equal "bigint(8)" - _(columns["boolean_col"].sql_type).must_equal "bit" - _(columns["decimal_col"].sql_type).must_equal "decimal(18,0)" - _(columns["float_col"].sql_type).must_equal "float" - _(columns["string_col"].sql_type).must_equal "nvarchar(4000)" - _(columns["text_col"].sql_type).must_equal "nvarchar(max)" + _(columns["integer_col"].sql_type).must_equal "int(4)" + _(columns["bigint_col"].sql_type).must_equal "bigint(8)" + _(columns["boolean_col"].sql_type).must_equal "bit" + _(columns["decimal_col"].sql_type).must_equal "decimal(18,0)" + _(columns["float_col"].sql_type).must_equal "float" + _(columns["string_col"].sql_type).must_equal "nvarchar(4000)" + _(columns["text_col"].sql_type).must_equal "nvarchar(max)" _(columns["datetime_nil_precision_col"].sql_type).must_equal "datetime" - _(columns["datetime_col"].sql_type).must_equal "datetime2(6)" - _(columns["timestamp_col"].sql_type).must_equal "datetime2(6)" - _(columns["time_col"].sql_type).must_equal "time(7)" - _(columns["date_col"].sql_type).must_equal "date" - _(columns["binary_col"].sql_type).must_equal "varbinary(max)" - - assert_line :integer_col, type: "integer" - assert_line :bigint_col, type: "bigint" - assert_line :boolean_col, type: "boolean" - assert_line :decimal_col, type: "decimal", precision: 18 - assert_line :float_col, type: "float" - assert_line :string_col, type: "string" - assert_line :text_col, type: "text" - assert_line :datetime_nil_precision_col, type: "datetime", precision: nil - assert_line :datetime_col, type: "datetime" - assert_line :datetime_col, type: "datetime" - assert_line :timestamp_col, type: "datetime" - assert_line :time_col, type: "time", precision: 7 - assert_line :date_col, type: "date" - assert_line :binary_col, type: "binary" + _(columns["datetime_col"].sql_type).must_equal "datetime2(6)" + _(columns["timestamp_col"].sql_type).must_equal "datetime2(6)" + _(columns["time_col"].sql_type).must_equal "time(7)" + _(columns["date_col"].sql_type).must_equal "date" + _(columns["binary_col"].sql_type).must_equal "varbinary(max)" + + assert_line :integer_col, type: "integer" + assert_line :bigint_col, type: "bigint" + assert_line :boolean_col, type: "boolean" + assert_line :decimal_col, type: "decimal", precision: 18 + assert_line :float_col, type: "float" + assert_line :string_col, type: "string" + assert_line :text_col, type: "text" + assert_line :datetime_nil_precision_col, type: "datetime", precision: nil + assert_line :datetime_col, type: "datetime" + assert_line :datetime_col, type: "datetime" + assert_line :timestamp_col, type: "datetime" + assert_line :time_col, type: "time", precision: 7 + assert_line :date_col, type: "date" + 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["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" + _(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 - generate_schema_for_table('sst_string_collation') + generate_schema_for_table("sst_string_collation") assert_line :string_without_collation, type: "string" assert_line :string_default_collation, type: "varchar" - assert_line :string_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS" - assert_line :varchar_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS" + assert_line :string_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS" + assert_line :varchar_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS" end # Special Cases @@ -141,7 +141,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase generate_schema_for_table("movies") do |output| match = output.match(%r{create_table "movies"(.*)do}) assert_not_nil(match, "non-standard primary key table not found") - assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved" + assert_match %r{primary_key: "movieid"}, match[1], "non-standard primary key not preserved" end end @@ -189,7 +189,7 @@ def generate_schema_for_table(*table_names) @generated_schema = stream.string yield @generated_schema if block_given? - @schema_lines = Hash.new + @schema_lines = {} type_matcher = /\A\s+t\.\w+\s+"(.*?)"[,\n]/ @generated_schema.each_line do |line| next unless line =~ type_matcher @@ -212,13 +212,13 @@ def assert_line(column_name, expected_options = {}) # Check that the expected and actual option keys. expected_options_keys = expected_options.keys expected_options_keys.delete(:type) - _(expected_options_keys.sort).must_equal (line.options.keys.sort), "For column '#{column_name}' expected schema options and actual schema options do not match." + _(expected_options_keys.sort).must_equal line.options.keys.sort, "For column '#{column_name}' expected schema options and actual schema options do not match." # Check the expected and actual option values. expected_options.each do |key, expected| - actual = key == :type ? line.send(:type_method) : line.send(key) + actual = (key == :type) ? line.send(:type_method) : line.send(key) - message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" + message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" if expected.nil? _(actual).must_be_nil message @@ -238,9 +238,9 @@ class SchemaLine LINE_PARSER = %r{t\.(\w+)\s+"(.*?)"[,\s+](.*)} attr_reader :line, - :type_method, - :col_name, - :options + :type_method, + :col_name, + :options def self.option(method_name) define_method(method_name) do @@ -283,7 +283,7 @@ def parse_line def parse_options(opts) if opts.present? - eval "{#{opts}}" + eval("{#{opts}}", binding, __FILE__, __LINE__) # standard:disable Security/Eval else {} end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 124c2d6dc..7057a9ca6 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -23,7 +23,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase columns = connection.columns("test.sst_schema_identity") assert_equal 2, columns.size - assert_equal 1, columns.select { |c| c.is_identity? }.size + assert_equal 1, columns.count { |c| c.is_identity? } end it "read only column properties for table in specific schema" do @@ -34,9 +34,9 @@ class SchemaTestSQLServer < ActiveRecord::TestCase assert_equal 7, test_columns.size assert_equal 2, dbo_columns.size assert_equal 2, columns.size - assert_equal 1, test_columns.select { |c| c.is_identity? }.size - assert_equal 1, dbo_columns.select { |c| c.is_identity? }.size - assert_equal 1, columns.select { |c| c.is_identity? }.size + assert_equal 1, test_columns.count { |c| c.is_identity? } + assert_equal 1, dbo_columns.count { |c| c.is_identity? } + assert_equal 1, columns.count { |c| c.is_identity? } end it "return correct varchar and nvarchar column limit length when table is in non-dbo schema" do @@ -50,7 +50,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase end describe "parsing table name from raw SQL" do - describe 'SELECT statements' do + describe "SELECT statements" do it do assert_equal "[sst_schema_columns]", connection.send(:get_raw_table_name, "SELECT [sst_schema_columns].[id] FROM [sst_schema_columns]") end @@ -72,7 +72,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase end end - describe 'INSERT statements' do + describe "INSERT statements" do it do assert_equal "[dashboards]", connection.send(:get_raw_table_name, "INSERT INTO [dashboards] DEFAULT VALUES; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident") end @@ -102,7 +102,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase end end - describe 'CREATE VIEW statements' do + 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 diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index c098e4c82..6c661e7a7 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -18,17 +18,15 @@ class TransactionTestSQLServer < ActiveRecord::TestCase end it "allow nested transactions to totally rollback" do - begin + Ship.transaction do + Ship.create! name: "Black Pearl" Ship.transaction do - Ship.create! name: "Black Pearl" - Ship.transaction do - Ship.create! name: "Flying Dutchman" - raise "HELL" - end + Ship.create! name: "Flying Dutchman" + raise "HELL" end - rescue Exception - assert_no_ships end + rescue + assert_no_ships end it "can use an isolation level and reverts back to starting isolation level" do diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index e964e0d96..bfa456c60 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -40,7 +40,7 @@ class SQLServerTriggerTest < ActiveRecord::TestCase end it "can insert into a table with composite pk with different data type with output inserted - with a hash setting for table name" do - exclude_output_inserted_table_names["sst_table_with_composite_pk_trigger_with_different_data_type"] = { pk_col_one: "uniqueidentifier", pk_col_two: "int" } + exclude_output_inserted_table_names["sst_table_with_composite_pk_trigger_with_different_data_type"] = {pk_col_one: "uniqueidentifier", pk_col_two: "int"} assert SSTestTriggerHistory.all.empty? obj = SSTestTriggerCompositePkWithDefferentDataType.create! pk_col_two: 123, event_name: "test trigger" _(obj.event_name).must_equal "test trigger" diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 4674e061f..c27f822f8 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -44,9 +44,9 @@ class UtilsTestSQLServer < ActiveRecord::TestCase ] } - let(:server_names) { valid_names.partition { |name| name =~ /server/ } } + let(:server_names) { valid_names.partition { |name| name =~ /server/ } } let(:database_names) { valid_names.partition { |name| name =~ /database/ } } - let(:schema_names) { valid_names.partition { |name| name =~ /schema/ } } + let(:schema_names) { valid_names.partition { |name| name =~ /schema/ } } it "extracts and returns #object identifier unquoted by default or quoted as needed" do valid_names.each do |n| @@ -61,7 +61,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase present, blank = send(:"#{part}_names") present.each do |n| name = extract_identifiers(n) - _(name.send(:"#{part}")).must_equal "#{part}", "With #{n.inspect} for ##{part} method" + _(name.send(:"#{part}")).must_equal part.to_s, "With #{n.inspect} for ##{part} method" _(name.send(:"#{part}_quoted")).must_equal "[#{part}]", "With #{n.inspect} for ##{part}_quoted method" end blank.each do |n| diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 84bfd80e1..88d195750 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -5,14 +5,18 @@ class ViewTestSQLServer < ActiveRecord::TestCase let(:connection) { ActiveRecord::Base.lease_connection } - describe 'view with default values' do + describe "view with default values" do before do - connection.drop_table :view_casing_table rescue nil + begin + connection.drop_table :view_casing_table + rescue + nil + end connection.create_table :view_casing_table, force: true do |t| - t.boolean :Default_Falsey, null: false, default: false - t.boolean :Default_Truthy, null: false, default: true - t.string :default_string_null, null: true, default: nil - t.string :default_string, null: false, default: "abc" + t.boolean :Default_Falsey, null: false, default: false + t.boolean :Default_Truthy, null: false, default: true + t.string :default_string_null, null: true, default: nil + t.string :default_string, null: false, default: "abc" end connection.execute("DROP VIEW IF EXISTS view_casing_table_view;") @@ -36,19 +40,19 @@ class ViewTestSQLServer < ActiveRecord::TestCase assert_equal false, obj.falsey assert_equal true, obj.truthy assert_equal "abc", obj.s - assert_nil obj.s_null + assert_nil obj.s_null assert_equal 0, klass.count obj.save! assert_equal false, obj.falsey assert_equal true, obj.truthy assert_equal "abc", obj.s - assert_nil obj.s_null + assert_nil obj.s_null assert_equal 1, klass.count end end - describe 'identity insert' do + describe "identity insert" do it "identity insert works with views" do assert_difference("SSTestCustomersView.count", 1) do SSTestCustomersView.create!(id: 5, name: "Bob") diff --git a/test/migrations/create_clients_and_change_column_collation.rb b/test/migrations/create_clients_and_change_column_collation.rb index 9e322d150..a14568c3a 100644 --- a/test/migrations/create_clients_and_change_column_collation.rb +++ b/test/migrations/create_clients_and_change_column_collation.rb @@ -9,8 +9,8 @@ def up t.timestamps end - change_column :clients, :name, :string, collation: 'SQL_Latin1_General_CP1_CS_AS' - change_column :clients, :code, :string, collation: 'SQL_Latin1_General_CP1_CI_AS' + change_column :clients, :name, :string, collation: "SQL_Latin1_General_CP1_CS_AS" + change_column :clients, :code, :string, collation: "SQL_Latin1_General_CP1_CI_AS" end def down diff --git a/test/models/sqlserver/edge_schema.rb b/test/models/sqlserver/edge_schema.rb index 9c7df7079..122908699 100644 --- a/test/models/sqlserver/edge_schema.rb +++ b/test/models/sqlserver/edge_schema.rb @@ -4,10 +4,10 @@ class SSTestEdgeSchema < ActiveRecord::Base self.table_name = "sst_edge_schemas" def with_spaces - read_attribute :'with spaces' + read_attribute :"with spaces" end def with_spaces=(value) - write_attribute :'with spaces', value + write_attribute :"with spaces", value end end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index a5160f791..367ae2bf8 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -7,36 +7,36 @@ create_table :sst_datatypes_migration, force: true do |t| # Simple Rails conventions. - t.integer :integer_col - t.bigint :bigint_col - t.boolean :boolean_col - t.decimal :decimal_col - t.float :float_col - t.string :string_col - t.text :text_col - t.datetime :datetime_nil_precision_col, precision: nil - t.datetime :datetime_col # Precision defaults to 6 + t.integer :integer_col + t.bigint :bigint_col + t.boolean :boolean_col + t.decimal :decimal_col + t.float :float_col + t.string :string_col + t.text :text_col + t.datetime :datetime_nil_precision_col, precision: nil + t.datetime :datetime_col # Precision defaults to 6 t.timestamp :timestamp_col # Precision defaults to 6 - t.time :time_col - t.date :date_col - t.binary :binary_col + t.time :time_col + t.date :date_col + t.binary :binary_col # Our type methods. - t.real :real_col - t.money :money_col - t.smalldatetime :smalldatetime_col - t.datetime2 :datetime2_col + t.real :real_col + t.money :money_col + t.smalldatetime :smalldatetime_col + t.datetime2 :datetime2_col t.datetimeoffset :datetimeoffset - t.smallmoney :smallmoney_col - t.char :char_col - t.varchar :varchar_col - t.text_basic :text_basic_col - 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 + t.smallmoney :smallmoney_col + t.char :char_col + t.varchar :varchar_col + t.text_basic :text_basic_col + 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 if supports_json? t.json :json_col else @@ -48,7 +48,7 @@ if ENV["IN_MEMORY_OLTP"] && supports_in_memory_oltp? create_table "sst_memory", force: true, id: false, - options: "WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)" do |t| + options: "WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)" do |t| t.primary_key_nonclustered :id t.string :name t.timestamps @@ -63,8 +63,8 @@ create_table "sst_uuids", force: true, id: :uuid do |t| t.string :name - t.uuid :other_uuid, default: "NEWID()" - t.uuid :uuid_nil_default, default: nil + t.uuid :other_uuid, default: "NEWID()" + t.uuid :uuid_nil_default, default: nil end create_table "sst_my$strange_table", force: true do |t| @@ -85,7 +85,7 @@ execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_quoted-view1') DROP VIEW [sst_quoted-view1]" execute "CREATE VIEW [sst_quoted-view1] AS SELECT * FROM [sst_quoted-table]" execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_quoted-view2') DROP VIEW [sst_quoted-view2]" - execute "CREATE VIEW [sst_quoted-view2] AS \n /*#{'x' * 4000}}*/ \n SELECT * FROM [sst_quoted-table]" + execute "CREATE VIEW [sst_quoted-view2] AS \n /*#{"x" * 4000}}*/ \n SELECT * FROM [sst_quoted-table]" create_table :sst_string_defaults, force: true do |t| t.column :string_with_null_default, :string, default: nil @@ -138,21 +138,33 @@ ) TINYITPKTABLE - execute "DROP DEFAULT [sst_getdateobject];" rescue nil - execute "CREATE DEFAULT [sst_getdateobject] AS getdate();" rescue nil + begin + execute "DROP DEFAULT [sst_getdateobject];" + rescue + nil + end + begin + execute "CREATE DEFAULT [sst_getdateobject] AS getdate();" + rescue + nil + end create_table "sst_defaultobjects", force: true do |t| t.string :name - t.date :date + t.date :date end execute "sp_bindefault 'sst_getdateobject', 'sst_defaultobjects.date'" - execute "DROP PROCEDURE my_getutcdate" rescue nil + begin + execute "DROP PROCEDURE my_getutcdate" + rescue + nil + end execute <<-SQL CREATE PROCEDURE my_getutcdate AS SELECT GETUTCDATE() utcdate SQL - create_table 'A Table With Spaces', force: true do |t| + create_table "A Table With Spaces", force: true do |t| t.string :name end @@ -195,7 +207,7 @@ execute <<-STRINGDEFAULTSBIGVIEW CREATE VIEW sst_string_defaults_big_view AS SELECT id, string_with_pretend_null_one as pretend_null - /*#{'x' * 4000}}*/ + /*#{"x" * 4000}}*/ FROM sst_string_defaults STRINGDEFAULTSBIGVIEW diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index 5419cc5a0..37a175db7 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -20,32 +20,32 @@ def coerce_tests!(*methods) def coerce_all_tests! instance_methods(false).each do |method| - next unless method.to_s =~ /\Atest/ + next unless method.to_s.start_with?("test") undef_method(method) end - STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{name}" + $stdout.puts "🙉 🙈 🙊 Undefined all tests: #{name}" end private def coerced_test_warning(test_to_coerce) - if test_to_coerce.is_a?(Regexp) - method = instance_methods(false).select { |m| m =~ test_to_coerce } + method = if test_to_coerce.is_a?(Regexp) + instance_methods(false).select { |m| m =~ test_to_coerce } else - method = test_to_coerce + test_to_coerce end Array(method).each do |m| result = if m && method_defined?(m) - alias_method("original_#{test_to_coerce.inspect.tr('/\:"', '')}", m) - undef_method(m) - end + alias_method("original_#{test_to_coerce.inspect.tr('/\:"', "")}", m) + undef_method(m) + end if result.blank? - STDOUT.puts "🐳 Unfound coerced test: #{name}##{m}" + $stdout.puts "🐳 Unfound coerced test: #{name}##{m}" else - STDOUT.puts "🐵 Undefined coerced test: #{name}##{m}" + $stdout.puts "🐵 Undefined coerced test: #{name}##{m}" end end end diff --git a/test/support/query_assertions.rb b/test/support/query_assertions.rb index accb11f09..e01c67bc4 100644 --- a/test/support/query_assertions.rb +++ b/test/support/query_assertions.rb @@ -16,7 +16,7 @@ def assert_queries_count(count = nil, include_schema: false, &block) if count assert_equal count, queries.size, "#{queries.size} instead of #{count} queries were executed. Queries: #{queries.join("\n\n")}" else - assert_operator queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}" + assert_operator queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{queries.empty? ? "" : "\nQueries:\n#{queries.join("\n")}"}" end result end @@ -31,7 +31,7 @@ def include_release_savepoint_placeholder_queries(queries) grouped_queries = [[]] queries.each do |query| - if query =~ /SAVE TRANSACTION \S+/ + if /SAVE TRANSACTION \S+/.match?(query) grouped_queries << [query] else grouped_queries.last << query @@ -39,7 +39,7 @@ def include_release_savepoint_placeholder_queries(queries) end grouped_queries.each do |group| - group.append "/* release savepoint placeholder for testing */" if group.first =~ /SAVE TRANSACTION \S+/ + group.append "/* release savepoint placeholder for testing */" if /SAVE TRANSACTION \S+/.match?(group.first) end grouped_queries.flatten diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index 5d307ec25..8f0af5376 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -6,11 +6,9 @@ def env_ar_test_files return unless ENV["TEST_FILES_AR"] && !ENV["TEST_FILES_AR"].empty? - @env_ar_test_files ||= begin - ENV["TEST_FILES_AR"].split(",").map { |file| - File.join ARTest::SQLServer.root_activerecord, file.strip - }.sort - end + @env_ar_test_files ||= ENV["TEST_FILES_AR"].split(",").map { |file| + File.join ARTest::SQLServer.root_activerecord, file.strip + }.sort end def env_test_files @@ -24,11 +22,9 @@ def sqlserver_cases end def ar_cases - @ar_cases ||= begin - Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject { - |x| x.include?("/adapters/") || x.include?("/encryption/performance") - }.sort - end + @ar_cases ||= Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject { |x| + x.include?("/adapters/") || x.include?("/encryption/performance") + }.sort end def test_files